This is an automated email from the ASF dual-hosted git repository.
sshenoy 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 53dd35320e HDDS-8659. Ozone admin SCM CLI command for block tokens.
(#4747)
53dd35320e is described below
commit 53dd35320e008fdf8277b442c710e02aace988e7
Author: tanvipenumudy <[email protected]>
AuthorDate: Thu Jul 13 11:11:06 2023 +0530
HDDS-8659. Ozone admin SCM CLI command for block tokens. (#4747)
---
.../apache/hadoop/hdds/scm/client/ScmClient.java | 9 ++
.../hadoop/hdds/protocol/SecretKeyProtocolScm.java | 11 +++
.../SecretKeyProtocolClientSideTranslatorPB.java | 17 +++-
.../hdds/security/symmetric/SecretKeyManager.java | 13 ++-
.../apache/hadoop/hdds/utils/HddsServerUtil.java | 21 +++++
.../security/symmetric/SecretKeyManagerTest.java | 2 +-
.../src/main/proto/ScmSecretKeyProtocol.proto | 13 +++
.../SecretKeyProtocolServerSideTranslatorPB.java | 19 +++-
.../hdds/scm/security/SecretKeyManagerService.java | 2 +-
.../hdds/scm/server/SCMSecurityProtocolServer.java | 16 +++-
.../hdds/scm/cli/ContainerOperationClient.java | 16 +++-
.../org/apache/hadoop/hdds/scm/cli/ScmOption.java | 4 +-
.../hadoop/ozone/insight/BaseInsightPoint.java | 4 +-
.../apache/hadoop/ozone/TestBlockTokensCLI.java | 105 ++++++++++++++++++++-
.../ozone/admin/scm/RotateKeySubCommand.java | 63 +++++++++++++
.../apache/hadoop/ozone/admin/scm/ScmAdmin.java | 3 +-
16 files changed, 301 insertions(+), 17 deletions(-)
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
index 1dffca6d57..179bf489cb 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/client/ScmClient.java
@@ -362,6 +362,15 @@ public interface ScmClient extends Closeable {
*/
List<String> getScmRatisRoles() throws IOException;
+ /**
+ * Force generates new secret keys (rotate).
+ *
+ * @param force boolean flag that forcefully rotates the key on demand
+ * @return
+ * @throws IOException
+ */
+ boolean rotateSecretKeys(boolean force) throws IOException;
+
/**
* Transfer the raft leadership.
*
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SecretKeyProtocolScm.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SecretKeyProtocolScm.java
index 9439c2ede0..51c1b7b8a0 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SecretKeyProtocolScm.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocol/SecretKeyProtocolScm.java
@@ -18,6 +18,8 @@ package org.apache.hadoop.hdds.protocol;
import org.apache.hadoop.security.KerberosInfo;
+import java.io.IOException;
+
import static
org.apache.hadoop.hdds.scm.ScmConfig.ConfigStrings.HDDS_SCM_KERBEROS_PRINCIPAL_KEY;
/**
@@ -28,4 +30,13 @@ import static
org.apache.hadoop.hdds.scm.ScmConfig.ConfigStrings.HDDS_SCM_KERBER
clientPrincipal = HDDS_SCM_KERBEROS_PRINCIPAL_KEY
)
public interface SecretKeyProtocolScm extends SecretKeyProtocol {
+
+ /**
+ * Force generates new secret keys (rotate).
+ *
+ * @param force boolean flag that forcefully rotates the key on demand
+ * @return key rotation status
+ * @throws IOException
+ */
+ boolean checkAndRotate(boolean force) throws IOException;
}
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SecretKeyProtocolClientSideTranslatorPB.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SecretKeyProtocolClientSideTranslatorPB.java
index a0206555e3..64badeb634 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SecretKeyProtocolClientSideTranslatorPB.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/protocolPB/SecretKeyProtocolClientSideTranslatorPB.java
@@ -20,6 +20,7 @@ import com.google.common.base.Preconditions;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import org.apache.hadoop.hdds.protocol.SecretKeyProtocol;
+import org.apache.hadoop.hdds.protocol.SecretKeyProtocolScm;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
import org.apache.hadoop.hdds.protocol.proto.SCMSecretKeyProtocolProtos;
import
org.apache.hadoop.hdds.protocol.proto.SCMSecretKeyProtocolProtos.SCMGetSecretKeyRequest;
@@ -50,7 +51,7 @@ import java.util.stream.Collectors;
* {@link SecretKeyProtocol} to the server proxy.
*/
public class SecretKeyProtocolClientSideTranslatorPB implements
- SecretKeyProtocol, ProtocolTranslator, Closeable {
+ SecretKeyProtocol, SecretKeyProtocolScm, ProtocolTranslator, Closeable {
/**
* RpcController is not used and hence is set to null.
@@ -129,6 +130,20 @@ public class SecretKeyProtocolClientSideTranslatorPB
implements
return ManagedSecretKey.fromProtobuf(secretKeyProto);
}
+ @Override
+ public boolean checkAndRotate(boolean force) throws IOException {
+ SCMSecretKeyProtocolProtos.SCMGetCheckAndRotateRequest request =
+ SCMSecretKeyProtocolProtos.SCMGetCheckAndRotateRequest.newBuilder()
+ .setForce(force)
+ .build();
+ boolean checkAndRotateStatus =
+ submitRequest(Type.CheckAndRotate, builder ->
+ builder.setCheckAndRotateRequest(request))
+ .getCheckAndRotateResponseProto().getStatus();
+
+ return checkAndRotateStatus;
+ }
+
@Override
public ManagedSecretKey getSecretKey(UUID id) throws IOException {
SCMGetSecretKeyRequest request = SCMGetSecretKeyRequest.newBuilder()
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManager.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManager.java
index 227317f913..20432f2a6a 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManager.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManager.java
@@ -109,23 +109,30 @@ public class SecretKeyManager implements SecretKeyClient {
*
* @return true if rotation actually happens, false if it doesn't.
*/
- public synchronized boolean checkAndRotate() throws SCMException {
+ public synchronized boolean checkAndRotate(boolean force)
+ throws SCMException {
// Initialize the state if it's not initialized already.
checkAndInitialize();
ManagedSecretKey currentKey = state.getCurrentKey();
- if (shouldRotate(currentKey)) {
+ if (force || shouldRotate(currentKey)) {
ManagedSecretKey newCurrentKey = generateSecretKey();
List<ManagedSecretKey> updatedKeys = state.getSortedKeys()
.stream().filter(x -> !x.isExpired())
.collect(toList());
updatedKeys.add(newCurrentKey);
- LOG.info("SecretKey rotation is happening, new key generated {}",
+ LOG.info((force ? "Forced " : "") +
+ "SecretKey rotation is happening, new key generated {}",
newCurrentKey);
state.updateKeys(updatedKeys);
return true;
}
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "The latest key was created at: " + currentKey.getCreationTime() +
+ " which does not pass the rotation duration");
+ }
return false;
}
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java
index 2c59bc1e3c..70d394e73b 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/HddsServerUtil.java
@@ -44,6 +44,7 @@ import org.apache.hadoop.hdds.DFSConfigKeysLegacy;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.SCMSecurityProtocol;
+import org.apache.hadoop.hdds.protocol.SecretKeyProtocolScm;
import
org.apache.hadoop.hdds.protocolPB.SecretKeyProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdds.protocolPB.SecretKeyProtocolDatanodePB;
import org.apache.hadoop.hdds.protocolPB.SecretKeyProtocolOmPB;
@@ -488,6 +489,26 @@ public final class HddsServerUtil {
SCMSecurityProtocolClientSideTranslatorPB.class, conf);
}
+ /**
+ * Creates a {@link org.apache.hadoop.hdds.protocol.SecretKeyProtocolScm}
+ * intended to be used by clients under the SCM identity.
+ *
+ * @param conf - Ozone configuration
+ * @return {@link org.apache.hadoop.hdds.protocol.SecretKeyProtocolScm}
+ * @throws IOException
+ */
+ public static SecretKeyProtocolScm getSecretKeyClientForSCM(
+ ConfigurationSource conf) throws IOException {
+ SecretKeyProtocolClientSideTranslatorPB scmSecretClient =
+ new SecretKeyProtocolClientSideTranslatorPB(
+ new SecretKeyProtocolFailoverProxyProvider(conf,
+ UserGroupInformation.getCurrentUser(),
+ SecretKeyProtocolScmPB.class), SecretKeyProtocolScmPB.class);
+
+ return TracingUtil.createProxy(scmSecretClient,
+ SecretKeyProtocolScm.class, conf);
+ }
+
/**
* Create a {@link org.apache.hadoop.hdds.protocol.SecretKeyProtocol} for
* datanode service, should be use only if user is the Datanode identity.
diff --git
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManagerTest.java
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManagerTest.java
index 675488f5e6..6f1a43e948 100644
---
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManagerTest.java
+++
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/symmetric/SecretKeyManagerTest.java
@@ -162,7 +162,7 @@ public class SecretKeyManagerTest {
ManagedSecretKey initialCurrentKey = state.getCurrentKey();
Mockito.reset(mockedKeyStore);
- assertEquals(expectRotate, lifeCycleManager.checkAndRotate());
+ assertEquals(expectRotate, lifeCycleManager.checkAndRotate(false));
if (expectRotate) {
// Verify rotation behavior.
diff --git
a/hadoop-hdds/interface-server/src/main/proto/ScmSecretKeyProtocol.proto
b/hadoop-hdds/interface-server/src/main/proto/ScmSecretKeyProtocol.proto
index 88b00ff7c3..010e38ee99 100644
--- a/hadoop-hdds/interface-server/src/main/proto/ScmSecretKeyProtocol.proto
+++ b/hadoop-hdds/interface-server/src/main/proto/ScmSecretKeyProtocol.proto
@@ -46,6 +46,8 @@ message SCMSecretKeyRequest {
optional string traceID = 2;
optional SCMGetSecretKeyRequest getSecretKeyRequest = 3;
+
+ optional SCMGetCheckAndRotateRequest checkAndRotateRequest = 4;
}
message SCMSecretKeyResponse {
@@ -67,12 +69,15 @@ message SCMSecretKeyResponse {
optional SCMSecretKeysListResponse secretKeysListResponseProto = 13;
+ optional SCMGetCheckAndRotateResponse checkAndRotateResponseProto = 14;
+
}
enum Type {
GetCurrentSecretKey = 1;
GetSecretKey = 2;
GetAllSecretKeys = 3;
+ CheckAndRotate = 4;
}
enum Status {
@@ -98,6 +103,10 @@ message SCMGetSecretKeyRequest {
required UUID secretKeyId = 1;
}
+message SCMGetCheckAndRotateRequest {
+ optional bool force = 1 [default = false];
+}
+
message SCMGetCurrentSecretKeyResponse {
required ManagedSecretKey secretKey = 1;
}
@@ -109,3 +118,7 @@ message SCMGetSecretKeyResponse {
message SCMSecretKeysListResponse {
repeated ManagedSecretKey secretKeys = 1;
}
+
+message SCMGetCheckAndRotateResponse {
+ optional bool status = 1;
+}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SecretKeyProtocolServerSideTranslatorPB.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SecretKeyProtocolServerSideTranslatorPB.java
index 527d7a42fa..e08d2be5f3 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SecretKeyProtocolServerSideTranslatorPB.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/protocol/SecretKeyProtocolServerSideTranslatorPB.java
@@ -19,7 +19,8 @@ package org.apache.hadoop.hdds.scm.protocol;
import com.google.protobuf.ProtocolMessageEnum;
import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
-import org.apache.hadoop.hdds.protocol.SecretKeyProtocol;
+import org.apache.hadoop.hdds.protocol.SecretKeyProtocolScm;
+import
org.apache.hadoop.hdds.protocol.proto.SCMSecretKeyProtocolProtos.SCMGetCheckAndRotateResponse;
import
org.apache.hadoop.hdds.protocol.proto.SCMSecretKeyProtocolProtos.SCMGetCurrentSecretKeyResponse;
import
org.apache.hadoop.hdds.protocol.proto.SCMSecretKeyProtocolProtos.SCMGetSecretKeyRequest;
import
org.apache.hadoop.hdds.protocol.proto.SCMSecretKeyProtocolProtos.SCMGetSecretKeyResponse;
@@ -52,13 +53,13 @@ public class SecretKeyProtocolServerSideTranslatorPB
private static final Logger LOG =
LoggerFactory.getLogger(SecretKeyProtocolServerSideTranslatorPB.class);
- private final SecretKeyProtocol impl;
+ private final SecretKeyProtocolScm impl;
private final StorageContainerManager scm;
private OzoneProtocolMessageDispatcher<SCMSecretKeyRequest,
SCMSecretKeyResponse, ProtocolMessageEnum> dispatcher;
- public SecretKeyProtocolServerSideTranslatorPB(SecretKeyProtocol impl,
+ public SecretKeyProtocolServerSideTranslatorPB(SecretKeyProtocolScm impl,
StorageContainerManager storageContainerManager,
ProtocolMessageMetrics messageMetrics) {
this.impl = impl;
@@ -102,6 +103,12 @@ public class SecretKeyProtocolServerSideTranslatorPB
.setSecretKeysListResponseProto(getAllSecretKeys())
.build();
+ case CheckAndRotate:
+ return scmSecurityResponse
+ .setCheckAndRotateResponseProto(
+ checkAndRotate(request.getCheckAndRotateRequest().getForce()))
+ .build();
+
default:
throw new IllegalArgumentException(
"Unknown request type: " + request.getCmdType());
@@ -153,6 +160,12 @@ public class SecretKeyProtocolServerSideTranslatorPB
.build();
}
+ private SCMGetCheckAndRotateResponse checkAndRotate(boolean force)
+ throws IOException {
+ return SCMGetCheckAndRotateResponse.newBuilder()
+ .setStatus(impl.checkAndRotate(force)).build();
+ }
+
private Status exceptionToResponseStatus(IOException ex) {
if (ex instanceof SCMSecretKeyException) {
return Status.values()[
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/SecretKeyManagerService.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/SecretKeyManagerService.java
index b81b4eab13..0df2ddef81 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/SecretKeyManagerService.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/SecretKeyManagerService.java
@@ -126,7 +126,7 @@ public class SecretKeyManagerService implements SCMService,
Runnable {
}
try {
- secretKeyManager.checkAndRotate();
+ secretKeyManager.checkAndRotate(false);
} catch (Exception e) {
LOG.error("Error occurred when updating SecretKeys.", e);
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
index 6fd44a4c0f..588177a0d7 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMSecurityProtocolServer.java
@@ -39,7 +39,7 @@ import java.util.stream.Collectors;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
-import org.apache.hadoop.hdds.protocol.SecretKeyProtocol;
+import org.apache.hadoop.hdds.protocol.SecretKeyProtocolScm;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.DatanodeDetailsProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeDetailsProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeType;
@@ -98,7 +98,7 @@ import static
org.apache.hadoop.hdds.security.x509.certificate.utils.Certificate
serverPrincipal = ScmConfig.ConfigStrings.HDDS_SCM_KERBEROS_PRINCIPAL_KEY)
@InterfaceAudience.Private
public class SCMSecurityProtocolServer implements SCMSecurityProtocol,
- SecretKeyProtocol {
+ SecretKeyProtocolScm {
private static final Logger LOGGER = LoggerFactory
.getLogger(SCMSecurityProtocolServer.class);
@@ -248,6 +248,18 @@ public class SCMSecurityProtocolServer implements
SCMSecurityProtocol,
}
}
+ @Override
+ public boolean checkAndRotate(boolean force) throws SCMSecretKeyException {
+ validateSecretKeyStatus();
+ try {
+ return secretKeyManager.checkAndRotate(force);
+ } catch (SCMException ex) {
+ LOGGER.error("Error rotating secret keys", ex);
+ throw new SCMSecretKeyException(ex.getMessage(),
+ SCMSecretKeyException.ErrorCode.INTERNAL_ERROR);
+ }
+ }
+
@Override
public synchronized List<String> getAllRootCaCertificates()
throws IOException {
diff --git
a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
index e8b657ecb1..e10d1d5b83 100644
---
a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
+++
b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerOperationClient.java
@@ -23,6 +23,7 @@ import org.apache.hadoop.conf.StorageUnit;
import org.apache.hadoop.hdds.client.ReplicationConfig;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.protocol.SecretKeyProtocolScm;
import
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ContainerDataProto;
import
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.ReadContainerResponseProto;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
@@ -43,6 +44,7 @@ import org.apache.hadoop.hdds.scm.pipeline.Pipeline;
import org.apache.hadoop.hdds.scm.protocol.StorageContainerLocationProtocol;
import org.apache.hadoop.hdds.scm.storage.ContainerProtocolCalls;
import org.apache.hadoop.hdds.utils.HAUtils;
+import org.apache.hadoop.hdds.utils.HddsServerUtil;
import org.apache.hadoop.ozone.ClientVersion;
import org.apache.hadoop.ozone.OzoneSecurityUtil;
import org.apache.hadoop.ozone.upgrade.UpgradeFinalizer.StatusAndMessages;
@@ -73,6 +75,7 @@ public class ContainerOperationClient implements ScmClient {
private final HddsProtos.ReplicationType replicationType;
private final StorageContainerLocationProtocol
storageContainerLocationClient;
+ private final SecretKeyProtocolScm secretKeyClient;
private final boolean containerTokenEnabled;
private final OzoneConfiguration configuration;
private XceiverClientManager xceiverClientManager;
@@ -85,9 +88,10 @@ public class ContainerOperationClient implements ScmClient {
return xceiverClientManager;
}
- public ContainerOperationClient(OzoneConfiguration conf) {
+ public ContainerOperationClient(OzoneConfiguration conf) throws IOException {
this.configuration = conf;
storageContainerLocationClient = newContainerRpcClient(conf);
+ secretKeyClient = newSecretKeyClient(conf);
containerSizeB = (int) conf.getStorageSize(OZONE_SCM_CONTAINER_SIZE,
OZONE_SCM_CONTAINER_SIZE_DEFAULT, StorageUnit.BYTES);
boolean useRatis = conf.getBoolean(
@@ -124,6 +128,11 @@ public class ContainerOperationClient implements ScmClient
{
return HAUtils.getScmContainerClient(configSource);
}
+ public static SecretKeyProtocolScm newSecretKeyClient(
+ ConfigurationSource configSource) throws IOException {
+ return HddsServerUtil.getSecretKeyClientForSCM(configSource);
+ }
+
@Override
public ContainerWithPipeline createContainer(String owner)
throws IOException {
@@ -466,6 +475,11 @@ public class ContainerOperationClient implements ScmClient
{
return storageContainerLocationClient.getScmInfo().getRatisPeerRoles();
}
+ @Override
+ public boolean rotateSecretKeys(boolean force) throws IOException {
+ return secretKeyClient.checkAndRotate(force);
+ }
+
@Override
public void transferLeadership(String newLeaderId) throws IOException {
storageContainerLocationClient.transferLeadership(newLeaderId);
diff --git
a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ScmOption.java
b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ScmOption.java
index 14744e5f80..dea8ac0ec8 100644
---
a/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ScmOption.java
+++
b/hadoop-hdds/tools/src/main/java/org/apache/hadoop/hdds/scm/cli/ScmOption.java
@@ -52,7 +52,7 @@ public class ScmOption {
"ServiceId of SCM HA Cluster")
private String scmServiceId;
- public ScmClient createScmClient() {
+ public ScmClient createScmClient() throws IOException {
GenericParentCommand parent = (GenericParentCommand)
spec.root().userObject();
OzoneConfiguration conf = parent.createOzoneConfiguration();
@@ -61,7 +61,7 @@ public class ScmOption {
return new ContainerOperationClient(conf);
}
- public ScmClient createScmClient(OzoneConfiguration conf) {
+ public ScmClient createScmClient(OzoneConfiguration conf) throws IOException
{
checkAndSetSCMAddressArg(conf);
return new ContainerOperationClient(conf);
}
diff --git
a/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/BaseInsightPoint.java
b/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/BaseInsightPoint.java
index 2a4cf88798..2789d03bfc 100644
---
a/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/BaseInsightPoint.java
+++
b/hadoop-ozone/insight/src/main/java/org/apache/hadoop/ozone/insight/BaseInsightPoint.java
@@ -17,6 +17,7 @@
*/
package org.apache.hadoop.ozone.insight;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -68,7 +69,8 @@ public abstract class BaseInsightPoint implements
InsightPoint {
/**
* Create scm client.
*/
- public ScmClient createScmClient(OzoneConfiguration ozoneConf) {
+ public ScmClient createScmClient(OzoneConfiguration ozoneConf)
+ throws IOException {
if (!HddsUtils.getHostNameFromConfigKeys(ozoneConf,
ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY).isPresent()) {
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestBlockTokensCLI.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestBlockTokensCLI.java
index c53ef2ea18..032c3dc779 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestBlockTokensCLI.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestBlockTokensCLI.java
@@ -17,12 +17,14 @@
*/
package org.apache.hadoop.ozone;
+import com.google.common.collect.Maps;
import org.apache.hadoop.hdds.annotation.InterfaceAudience;
import org.apache.hadoop.hdds.cli.OzoneAdmin;
import org.apache.hadoop.hdds.conf.DefaultConfigManager;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.scm.ScmConfig;
import org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig;
+import org.apache.hadoop.hdds.security.symmetric.ManagedSecretKey;
import org.apache.hadoop.hdds.security.symmetric.SecretKeyManager;
import org.apache.hadoop.hdds.utils.IOUtils;
import org.apache.hadoop.minikdc.MiniKdc;
@@ -35,6 +37,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
import org.junit.rules.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,10 +48,15 @@ import java.io.UnsupportedEncodingException;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.TimeoutException;
+import static java.time.Duration.between;
import static
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION;
import static
org.apache.hadoop.hdds.DFSConfigKeysLegacy.DFS_DATANODE_KERBEROS_KEYTAB_FILE_KEY;
import static
org.apache.hadoop.hdds.DFSConfigKeysLegacy.DFS_DATANODE_KERBEROS_PRINCIPAL_KEY;
@@ -59,6 +67,7 @@ import static
org.apache.hadoop.hdds.scm.ScmConfig.ConfigStrings.HDDS_SCM_KERBER
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_CLIENT_ADDRESS_KEY;
import static
org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig.ConfigStrings.HDDS_SCM_HTTP_KERBEROS_KEYTAB_FILE_KEY;
import static
org.apache.hadoop.hdds.scm.server.SCMHTTPServerConfig.ConfigStrings.HDDS_SCM_HTTP_KERBEROS_PRINCIPAL_KEY;
+import static
org.apache.hadoop.hdds.security.symmetric.SecretKeyConfig.parseRotateDuration;
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.OZONE_OM_HTTP_KERBEROS_KEYTAB_FILE;
@@ -219,6 +228,100 @@ public final class TestBlockTokensCLI {
return null;
}
+ private boolean isForceFlagPresent(String[] args) {
+ for (String arg : args) {
+ if (arg.equals("--force")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Test
+ public void testRotateKeySCMAdminCommandWithForceFlag()
+ throws InterruptedException, TimeoutException {
+ GenericTestUtils.waitFor(() -> cluster.getScmLeader() != null, 100, 1000);
+ InetSocketAddress address = cluster.getScmLeader().getClientRpcAddress();
+ String hostPort = address.getHostName() + ":" + address.getPort();
+
+ testRotateKeySCMAdminCommandUtil(createArgsForCommand(
+ new String[]{"scm", "rotate", "--scm", hostPort, "--force"}));
+ }
+
+ @Test
+ public void testRotateKeySCMAdminCommandWithoutForceFlag()
+ throws InterruptedException, TimeoutException {
+ GenericTestUtils.waitFor(() -> cluster.getScmLeader() != null, 100, 1000);
+ InetSocketAddress address = cluster.getScmLeader().getClientRpcAddress();
+ String hostPort = address.getHostName() + ":" + address.getPort();
+
+ testRotateKeySCMAdminCommandUtil(
+ createArgsForCommand(new String[]{"scm", "rotate", "--scm",
hostPort}));
+ }
+
+ public void testRotateKeySCMAdminCommandUtil(String[] args) {
+ // Get the initial secret key.
+ String initialKey =
+ getScmSecretKeyManager().getCurrentSecretKey().toString();
+
+ // Assert that both initial key and subsequent key are the same before
+ // rotating.
+ String currentKey =
+ getScmSecretKeyManager().getCurrentSecretKey().toString();
+ Assertions.assertEquals(initialKey, currentKey);
+
+ // Rotate the secret key.
+ ozoneAdmin.execute(args);
+
+ // Get the new secret key.
+ String newKey =
+ getScmSecretKeyManager().getCurrentSecretKey().toString();
+
+ // Assert that the old key and new key are not the same after rotating if
+ // either:
+ // 1. The rotation duration has surpassed, or
+ // 2. The --force flag has been passed.
+ // Otherwise, both keys should be the same.
+ if (isForceFlagPresent(args) ||
+ shouldRotate(getScmSecretKeyManager().getCurrentSecretKey())) {
+ Assertions.assertNotEquals(initialKey, newKey);
+ } else {
+ Assertions.assertEquals(initialKey, newKey);
+ }
+ }
+
+ private String getSetConfStringFromConf(String key) {
+ return String.format("--set=%s=%s", key, conf.get(key));
+ }
+
+ public boolean shouldRotate(ManagedSecretKey currentKey) {
+ Duration established = between(currentKey.getCreationTime(),
Instant.now());
+ return established.compareTo(parseRotateDuration(conf)) >= 0;
+ }
+
+ /**
+ * Since ScmAdmin relies on ScmOption to generate configurations, it uses the
+ * default configuration obtained from
+ * {@link org.apache.hadoop.hdds.scm.cli.ScmOption#createScmClient(
+ * OzoneConfiguration)} using createOzoneConfiguration().
+ * In the absence of a better way to pass these configurations via
integration
+ * tests in the ozone admin shell, this method handles the passing of
kerberos
+ * and SCM HA configurations by creating strings in the "--set=key=value"
+ * format.
+ */
+ private String[] createArgsForCommand(String[] additionalArgs) {
+ OzoneConfiguration defaultConf = ozoneAdmin.createOzoneConfiguration();
+ Map<String, String> diff =
Maps.difference(defaultConf.getOzoneProperties(),
+ conf.getOzoneProperties()).entriesOnlyOnRight();
+ String[] args = new String[diff.size() + additionalArgs.length];
+ int i = 0;
+ for (Map.Entry<String, String> entry : diff.entrySet()) {
+ args[i++] = getSetConfStringFromConf(entry.getKey());
+ }
+ System.arraycopy(additionalArgs, 0, args, i, additionalArgs.length);
+ return args;
+ }
+
private static void startCluster()
throws IOException, TimeoutException, InterruptedException {
OzoneManager.setTestSecureOmFlag(true);
@@ -229,7 +332,7 @@ public final class TestBlockTokensCLI {
.setScmId(scmId)
.setNumDatanodes(3)
.setNumOfStorageContainerManagers(3)
- .setNumOfOzoneManagers(1);
+ .setNumOfOzoneManagers(3);
cluster = (MiniOzoneHAClusterImpl) builder.build();
cluster.waitForClusterToBeReady();
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/scm/RotateKeySubCommand.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/scm/RotateKeySubCommand.java
new file mode 100644
index 0000000000..7e6e4104a9
--- /dev/null
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/scm/RotateKeySubCommand.java
@@ -0,0 +1,63 @@
+/*
+ * 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.hadoop.ozone.admin.scm;
+
+import java.io.IOException;
+
+import org.apache.hadoop.hdds.cli.HddsVersionProvider;
+import org.apache.hadoop.hdds.scm.cli.ContainerOperationClient;
+import org.apache.hadoop.hdds.scm.cli.ScmSubcommand;
+import org.apache.hadoop.hdds.scm.client.ScmClient;
+import picocli.CommandLine;
+
+/**
+ * Handler of ozone admin scm rotate command.
+ */
[email protected](
+ name = "rotate",
+ description = "CLI command to force generate new keys (rotate)",
+ mixinStandardHelpOptions = true,
+ versionProvider = HddsVersionProvider.class)
+public class RotateKeySubCommand extends ScmSubcommand {
+
+ @CommandLine.Option(names = "--force",
+ description = "Force generate new keys")
+ private boolean force = false;
+
+ @CommandLine.ParentCommand
+ private ScmAdmin parent;
+
+ @Override
+ protected void execute(ScmClient scmClient) throws IOException {
+ try (ScmClient client = new ContainerOperationClient(
+ parent.getParent().getOzoneConf())) {
+ boolean status = false;
+ try {
+ status = client.rotateSecretKeys(force);
+ } catch (IOException e) {
+ System.err.println("Secret key rotation failed: " + e.getMessage());
+ return;
+ }
+ if (status) {
+ System.out.println("Secret key rotation is complete. A new key has " +
+ "been generated.");
+ }
+ }
+ }
+}
+
diff --git
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/scm/ScmAdmin.java
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/scm/ScmAdmin.java
index fbc5a3b52b..98eba154b2 100644
---
a/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/scm/ScmAdmin.java
+++
b/hadoop-ozone/tools/src/main/java/org/apache/hadoop/ozone/admin/scm/ScmAdmin.java
@@ -40,7 +40,8 @@ import picocli.CommandLine.Spec;
FinalizationScmStatusSubcommand.class,
TransferScmLeaderSubCommand.class,
DeletedBlocksTxnCommands.class,
- DecommissionScmSubcommand.class
+ DecommissionScmSubcommand.class,
+ RotateKeySubCommand.class
})
@MetaInfServices(SubcommandWithParent.class)
public class ScmAdmin extends GenericCli implements SubcommandWithParent {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]