This is an automated email from the ASF dual-hosted git repository.
pifta 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 bd8bb39468 HDDS-9015. Block CSR request in SCM for
"hdds.x509.rootca.certificate.polling.interval" time period (#5064)
bd8bb39468 is described below
commit bd8bb39468ed1693b94b75ef85db4710cd693618
Author: Sammi Chen <[email protected]>
AuthorDate: Fri Aug 4 23:47:53 2023 +0800
HDDS-9015. Block CSR request in SCM for
"hdds.x509.rootca.certificate.polling.interval" time period (#5064)
---
.../hadoop/hdds/scm/exceptions/SCMException.java | 3 +-
.../client/DefaultCertificateClient.java | 10 +-
.../src/main/proto/ScmServerProtocol.proto | 1 +
.../java/org/apache/hadoop/hdds/scm/ScmUtils.java | 23 +++++
.../apache/hadoop/hdds/scm/ha/StatefulService.java | 8 ++
.../hdds/scm/ha/StatefulServiceStateManager.java | 9 ++
.../scm/ha/StatefulServiceStateManagerImpl.java | 11 +++
.../hdds/scm/security/RootCARotationManager.java | 107 ++++++++++++++++++++-
.../hdds/scm/server/SCMClientProtocolServer.java | 17 ++--
.../hdds/scm/server/SCMSecurityProtocolServer.java | 43 +++------
.../hdds/scm/server/StorageContainerManager.java | 9 +-
.../scm/security/TestRootCARotationManager.java | 83 ++++++++++++++++
12 files changed, 272 insertions(+), 52 deletions(-)
diff --git
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/exceptions/SCMException.java
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/exceptions/SCMException.java
index 1cfc288274..9144d85e07 100644
---
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/exceptions/SCMException.java
+++
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/exceptions/SCMException.java
@@ -136,6 +136,7 @@ public class SCMException extends IOException {
INVALID_PIPELINE_STATE,
DUPLICATED_PIPELINE_ID,
TIMEOUT,
- CA_ROTATION_IN_PROGRESS
+ CA_ROTATION_IN_PROGRESS,
+ CA_ROTATION_IN_POST_PROGRESS,
}
}
diff --git
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
index 6a950df524..a6583fc014 100644
---
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
+++
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/DefaultCertificateClient.java
@@ -43,6 +43,7 @@ import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
@@ -408,7 +409,9 @@ public abstract class DefaultCertificateClient implements
CertificateClient {
chain.add(lastInsertedCert);
List<X509Certificate> caCertList =
OzoneSecurityUtil.convertToX509(listCA());
- while (!getAllRootCaCerts().contains(lastInsertedCert)) {
+ Set<X509Certificate> rootCaCertList = getAllRootCaCerts();
+ while (!rootCaCertList.isEmpty() &&
+ !rootCaCertList.contains(lastInsertedCert)) {
Optional<X509Certificate> issuerOpt =
getIssuerForCert(lastInsertedCert, caCertList);
if (issuerOpt.isPresent()) {
@@ -1403,8 +1406,9 @@ public abstract class DefaultCertificateClient implements
CertificateClient {
public synchronized void setCACertificate(X509Certificate cert)
throws Exception {
caCertId = cert.getSerialNumber().toString();
+ String pemCert = CertificateCodec.getPEMEncodedString(cert);
certificateMap.put(caCertId,
- CertificateCodec.getCertPathFromPemEncodedString(
- CertificateCodec.getPEMEncodedString(cert)));
+ CertificateCodec.getCertPathFromPemEncodedString(pemCert));
+ pemEncodedCACerts = Arrays.asList(pemCert);
}
}
diff --git
a/hadoop-hdds/interface-server/src/main/proto/ScmServerProtocol.proto
b/hadoop-hdds/interface-server/src/main/proto/ScmServerProtocol.proto
index 0d468ed0ab..37f1d4adbf 100644
--- a/hadoop-hdds/interface-server/src/main/proto/ScmServerProtocol.proto
+++ b/hadoop-hdds/interface-server/src/main/proto/ScmServerProtocol.proto
@@ -134,6 +134,7 @@ enum Status {
DUPLICATED_PIPELINE_ID = 41;
TIMEOUT = 42;
CA_ROTATION_IN_PROGRESS = 43;
+ CA_ROTATION_IN_POST_PROGRESS = 44;
}
/**
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ScmUtils.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ScmUtils.java
index 152e8d38b5..27a97a0349 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ScmUtils.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ScmUtils.java
@@ -28,8 +28,10 @@ import org.apache.hadoop.hdds.scm.events.SCMEvents;
import org.apache.hadoop.hdds.scm.exceptions.SCMException;
import org.apache.hadoop.hdds.scm.safemode.Precheck;
+import org.apache.hadoop.hdds.scm.security.RootCARotationManager;
import org.apache.hadoop.hdds.scm.server.ContainerReportQueue;
import
org.apache.hadoop.hdds.scm.server.SCMDatanodeHeartbeatDispatcher.ContainerReport;
+import org.apache.hadoop.hdds.security.SecurityConfig;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.ozone.ha.ConfUtils;
import org.apache.hadoop.util.StringUtils;
@@ -225,4 +227,25 @@ public final class ScmUtils {
+ SCMEvents.INCREMENTAL_CONTAINER_REPORT.getName());
}
+ public static void checkIfCertSignRequestAllowed(
+ RootCARotationManager rotationManager, boolean isScmCertRenew,
+ OzoneConfiguration config, String operation) throws SCMException {
+ if (rotationManager != null) {
+ if (rotationManager.isRotationInProgress() && !isScmCertRenew) {
+ throw new SCMException("Root CA and Sub CA rotation is in-progress." +
+ " Please try the operation later again.",
+ SCMException.ResultCodes.CA_ROTATION_IN_PROGRESS);
+ }
+ if (rotationManager.isPostRotationInProgress()) {
+ SecurityConfig securityConfig = new SecurityConfig(config);
+ throw new SCMException("The operation " + operation +
+ " is prohibited due to root CA " +
+ "and sub CA rotation have just finished. " +
+ "The prohibition state will last at most " +
+ securityConfig.getRootCaCertificatePollingInterval() + ". " +
+ "Please try the operation later again.",
+ SCMException.ResultCodes.CA_ROTATION_IN_POST_PROGRESS);
+ }
+ }
+ }
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulService.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulService.java
index 12f285e6bc..eb1b7d56d1 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulService.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulService.java
@@ -82,4 +82,12 @@ public abstract class StatefulService implements SCMService {
}
}
+
+ /**
+ * Deletes the persisted configuration mapped to the specified serviceName.
+ * @throws IOException on failure
+ */
+ protected final void deleteConfiguration() throws IOException {
+ stateManager.deleteConfiguration(getServiceName());
+ }
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManager.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManager.java
index a6a8a50ae5..c13058c490 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManager.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManager.java
@@ -52,6 +52,15 @@ public interface StatefulServiceStateManager {
*/
ByteString readConfiguration(String serviceName) throws IOException;
+ /**
+ * Deletes the persisted configuration mapped to the specified serviceName.
+ * @param serviceName name of the {@link StatefulService}, obtained through
+ * {@link SCMService#getServiceName()}
+ * @throws IOException on failure
+ */
+ @Replicate
+ void deleteConfiguration(String serviceName) throws IOException;
+
/**
* Sets the updated reference to the table when reloading SCM state.
* @param statefulServiceConfig table from
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManagerImpl.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManagerImpl.java
index 1e7a756f41..abf3b72a13 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManagerImpl.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/ha/StatefulServiceStateManagerImpl.java
@@ -81,6 +81,17 @@ public final class StatefulServiceStateManagerImpl
return statefulServiceConfig.get(serviceName);
}
+ /**
+ * Deletes the persisted configuration mapped to the specified serviceName.
+ * @param serviceName name of the {@link StatefulService}, obtained through
+ * {@link SCMService#getServiceName()}
+ * @throws IOException on failure
+ */
+ @Override
+ public void deleteConfiguration(String serviceName) throws IOException {
+ statefulServiceConfig.delete(serviceName);
+ }
+
/**
* {@inheritDoc}
*/
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/RootCARotationManager.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/RootCARotationManager.java
index 0158eb1bc0..faa7889489 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/RootCARotationManager.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/security/RootCARotationManager.java
@@ -24,14 +24,16 @@ import
com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.proto.HddsProtos;
+import org.apache.hadoop.hdds.protocol.proto.HddsProtos.CertInfoProto;
import org.apache.hadoop.hdds.scm.ha.HASecurityUtils;
import org.apache.hadoop.hdds.scm.ha.SCMContext;
-import org.apache.hadoop.hdds.scm.ha.SCMService;
import org.apache.hadoop.hdds.scm.ha.SCMServiceException;
import org.apache.hadoop.hdds.scm.ha.SequenceIdGenerator;
+import org.apache.hadoop.hdds.scm.ha.StatefulService;
import org.apache.hadoop.hdds.scm.server.SCMStorageConfig;
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
import org.apache.hadoop.hdds.security.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.certificate.CertInfo;
import
org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateServer;
import
org.apache.hadoop.hdds.security.x509.certificate.authority.profile.DefaultCAProfile;
import
org.apache.hadoop.hdds.security.x509.certificate.client.SCMCertificateClient;
@@ -56,6 +58,7 @@ import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
+import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
@@ -75,7 +78,7 @@ import static
org.apache.hadoop.ozone.OzoneConsts.SCM_ROOT_CA_COMPONENT_NAME;
/**
* Root CA Rotation Service is a service in SCM to control the CA rotation.
*/
-public class RootCARotationManager implements SCMService {
+public class RootCARotationManager extends StatefulService {
public static final Logger LOG =
LoggerFactory.getLogger(RootCARotationManager.class);
@@ -89,11 +92,13 @@ public class RootCARotationManager implements SCMService {
private final Duration renewalGracePeriod;
private final Date timeOfDay;
private final Duration ackTimeout;
+ private final Duration rootCertPollInterval;
private final SCMCertificateClient scmCertClient;
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final AtomicBoolean isProcessing = new AtomicBoolean(false);
private final AtomicReference<Long> processStartTime =
new AtomicReference<>();
+ private final AtomicBoolean isPostProcessing = new AtomicBoolean(false);
private final String threadName = this.getClass().getSimpleName();
private final String newCAComponent = SCM_ROOT_CA_COMPONENT_NAME +
HDDS_NEW_KEY_CERT_DIR_NAME_SUFFIX +
@@ -105,6 +110,7 @@ public class RootCARotationManager implements SCMService {
private ScheduledFuture waitAckTask;
private ScheduledFuture waitAckTimeoutTask;
private final RootCARotationMetrics metrics;
+ private ScheduledFuture clearPostProcessingTask;
/**
* Constructs RootCARotationManager with the specified arguments.
@@ -131,6 +137,7 @@ public class RootCARotationManager implements SCMService {
* (4) Rotation Committed
*/
public RootCARotationManager(StorageContainerManager scm) {
+ super(scm.getStatefulServiceStateManager());
this.scm = scm;
this.ozoneConf = scm.getConfiguration();
this.secConf = new SecurityConfig(ozoneConf);
@@ -141,6 +148,7 @@ public class RootCARotationManager implements SCMService {
renewalGracePeriod = secConf.getRenewalGracePeriod();
timeOfDay = Date.from(LocalDateTime.parse(secConf.getCaRotationTimeOfDay())
.atZone(ZoneId.systemDefault()).toInstant());
+ rootCertPollInterval = secConf.getRootCaCertificatePollingInterval();
executorService = Executors.newScheduledThreadPool(1,
new ThreadFactoryBuilder().setNameFormat(threadName)
@@ -178,14 +186,25 @@ public class RootCARotationManager implements SCMService {
if (waitAckTimeoutTask != null) {
waitAckTask.cancel(true);
}
+ if (clearPostProcessingTask != null) {
+ clearPostProcessingTask.cancel(true);
+ }
isProcessing.set(false);
processStartTime.set(null);
+ isPostProcessing.set(false);
}
return;
}
if (isRunning.compareAndSet(false, true)) {
LOG.info("notifyStatusChanged: enable monitor task");
+ // enable post rotation task if needed.
+ try {
+ checkAndHandlePostProcessing();
+ } catch (IOException | CertificateException e) {
+ throw new RuntimeException(
+ "Error while checking post-processing state.", e);
+ }
}
return;
}
@@ -252,6 +271,10 @@ public class RootCARotationManager implements SCMService {
return isProcessing.get();
}
+ public boolean isPostRotationInProgress() {
+ return isPostProcessing.get();
+ }
+
/**
* Task to monitor certificate lifetime and start rotation if needed.
*/
@@ -662,14 +685,16 @@ public class RootCARotationManager implements SCMService {
processStartTime.set(null);
// save root certificate to certStore
+ X509Certificate rootCACert = null;
try {
if (scm.getCertificateStore().getCertificateByID(
rootCACertHolder.getSerialNumber(), VALID_CERTS) == null) {
LOG.info("Persist root certificate {} to cert store",
rootCACertId);
+ rootCACert =
+ CertificateCodec.getX509Certificate(rootCACertHolder);
scm.getCertificateStore().storeValidCertificate(
- rootCACertHolder.getSerialNumber(),
- CertificateCodec.getX509Certificate(rootCACertHolder),
+ rootCACertHolder.getSerialNumber(), rootCACert,
HddsProtos.NodeType.SCM);
}
} catch (CertificateException | IOException e) {
@@ -683,6 +708,17 @@ public class RootCARotationManager implements SCMService {
String msg = "Root certificate " + rootCACertId +
" rotation is finished successfully after " + timeTaken + "
ns";
cleanupAndStop(msg);
+
+ // set the isPostProcessing to true, which will block the CSR
+ // signing in this period.
+ enterPostProcessing(rootCertPollInterval.toMillis());
+ // save the new root certificate to rocksdb through ratis
+ if (rootCACert != null) {
+ saveConfiguration(new CertInfo.Builder()
+ .setX509Certificate(rootCACert)
+ .setTimestamp(rootCACert.getNotBefore().getTime())
+ .build().getProtobuf());
+ }
} catch (Throwable e) {
LOG.error("Execution error", e);
handler.resetRotationPrepareAcks();
@@ -695,6 +731,21 @@ public class RootCARotationManager implements SCMService {
}
}
+ private void enterPostProcessing(long delay) {
+ isPostProcessing.set(true);
+ LOG.info("isPostProcessing is true for {} ms", delay);
+ clearPostProcessingTask = executorService.schedule(() -> {
+ isPostProcessing.set(false);
+ LOG.info("isPostProcessing is false");
+ try {
+ deleteConfiguration();
+ LOG.info("Stateful configuration is deleted");
+ } catch (IOException e) {
+ LOG.error("Failed to delete stateful configuration", e);
+ }
+ }, delay, TimeUnit.MILLISECONDS);
+ }
+
/**
* Stops scheduled monitor task.
*/
@@ -728,4 +779,52 @@ public class RootCARotationManager implements SCMService {
}
return false;
}
+
+ private void checkAndHandlePostProcessing() throws IOException,
+ CertificateException {
+ CertInfoProto proto = readConfiguration(CertInfoProto.class);
+ if (proto == null) {
+ LOG.info("No {} configuration found in stateful storage",
+ getServiceName());
+ return;
+ }
+
+ X509Certificate cert =
+ CertificateCodec.getX509Certificate(proto.getX509Certificate());
+
+ List<X509Certificate> scmCertChain = scmCertClient.getTrustChain();
+ Preconditions.checkArgument(scmCertChain.size() > 1);
+ X509Certificate rootCert = scmCertChain.get(scmCertChain.size() - 1);
+
+ int result = rootCert.getSerialNumber().compareTo(cert.getSerialNumber());
+ if (result > 0) {
+ // this could happen if the previous stateful configuration is not
deleted
+ LOG.warn("Root CA certificate ID {} in stateful storage is smaller than"
+
+ " current scm's root certificate ID {}", cert.getSerialNumber(),
+ rootCert.getSerialNumber());
+
+ deleteConfiguration();
+ LOG.warn("Stateful configuration is deleted");
+ return;
+ } else if (result < 0) {
+ // this should not happen
+ throw new RuntimeException("Root CA certificate ID " +
+ cert.getSerialNumber() + " in stateful storage is bigger than " +
+ "current scm's root CA certificate ID " +
rootCert.getSerialNumber());
+ }
+
+ Date issueTime = rootCert.getNotBefore();
+ Date now = Calendar.getInstance().getTime();
+ Duration gap = Duration.between(issueTime.toInstant(), now.toInstant());
+ gap = gap.minus(rootCertPollInterval);
+ if (gap.isNegative()) {
+ long delay = -gap.toMillis();
+ enterPostProcessing(delay);
+ } else {
+ // this could happen if the service stopped for a long and restarts
+ LOG.info("Root CA certificate ID {} in stateful storage has already " +
+ "come out of post-processing state", cert.getSerialNumber());
+ deleteConfiguration();
+ }
+ }
}
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
index a344ad56fd..1da84af6ef 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/SCMClientProtocolServer.java
@@ -113,6 +113,7 @@ import java.util.stream.Stream;
import static
org.apache.hadoop.hdds.protocol.proto.StorageContainerLocationProtocolProtos.StorageContainerLocationProtocolService.newReflectiveBlockingService;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HANDLER_COUNT_DEFAULT;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_HANDLER_COUNT_KEY;
+import static
org.apache.hadoop.hdds.scm.ScmUtils.checkIfCertSignRequestAllowed;
import static
org.apache.hadoop.hdds.scm.ha.HASecurityUtils.createSCMRatisTLSConfig;
import static
org.apache.hadoop.hdds.scm.server.StorageContainerManager.startRpcServer;
import static org.apache.hadoop.hdds.server.ServerUtils.getRemoteUserName;
@@ -131,14 +132,14 @@ public class SCMClientProtocolServer implements
private final RPC.Server clientRpcServer;
private final InetSocketAddress clientRpcAddress;
private final StorageContainerManager scm;
+ private final OzoneConfiguration config;
private final ProtocolMessageMetrics<ProtocolMessageEnum> protocolMetrics;
- public SCMClientProtocolServer(
- OzoneConfiguration conf,
+ public SCMClientProtocolServer(OzoneConfiguration conf,
StorageContainerManager scm,
- ReconfigurationHandler reconfigurationHandler
- ) throws IOException {
+ ReconfigurationHandler reconfigurationHandler) throws IOException {
this.scm = scm;
+ this.config = conf;
final int handlerCount =
conf.getInt(OZONE_SCM_HANDLER_COUNT_KEY,
OZONE_SCM_HANDLER_COUNT_DEFAULT);
@@ -803,12 +804,8 @@ public class SCMClientProtocolServer implements
throw new SCMException("SCM HA not enabled.",
ResultCodes.INTERNAL_ERROR);
}
- if (scm.getRootCARotationManager() != null &&
- scm.getRootCARotationManager().isRotationInProgress()) {
- throw new SCMException(("Root CA and Sub CA rotation is in-progress." +
- " Please try the operation later again."),
- ResultCodes.CA_ROTATION_IN_PROGRESS);
- }
+ checkIfCertSignRequestAllowed(scm.getRootCARotationManager(),
+ false, config, "transferLeadership");
boolean auditSuccess = true;
final Map<String, String> auditMap = Maps.newHashMap();
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 a273d49495..180c4574a3 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
@@ -83,6 +83,7 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
+import static
org.apache.hadoop.hdds.scm.ScmUtils.checkIfCertSignRequestAllowed;
import static
org.apache.hadoop.hdds.security.exception.SCMSecretKeyException.ErrorCode.SECRET_KEY_NOT_ENABLED;
import static
org.apache.hadoop.hdds.security.exception.SCMSecretKeyException.ErrorCode.SECRET_KEY_NOT_INITIALIZED;
import static
org.apache.hadoop.hdds.security.exception.SCMSecurityException.ErrorCode.CERTIFICATE_NOT_FOUND;
@@ -111,6 +112,7 @@ public class SCMSecurityProtocolServer implements
SCMSecurityProtocol,
private final ProtocolMessageMetrics secretKeyMetrics;
private final StorageContainerManager storageContainerManager;
private final CertificateClient scmCertificateClient;
+ private final OzoneConfiguration config;
// SecretKey may not be enabled when neither block token nor container
// token is enabled.
@@ -127,6 +129,7 @@ public class SCMSecurityProtocolServer implements
SCMSecurityProtocol,
this.rootCertificateServer = rootCertificateServer;
this.scmCertificateServer = scmCertificateServer;
this.scmCertificateClient = scmCertClient;
+ this.config = conf;
this.secretKeyManager = secretKeyManager;
final int handlerCount =
conf.getInt(ScmConfigKeys.OZONE_SCM_SECURITY_HANDLER_COUNT_KEY,
@@ -190,13 +193,9 @@ public class SCMSecurityProtocolServer implements
SCMSecurityProtocol,
LOGGER.info("Processing CSR for dn {}, UUID: {}", dnDetails.getHostName(),
dnDetails.getUuid());
Objects.requireNonNull(dnDetails);
- if (storageContainerManager.getRootCARotationManager() != null &&
- storageContainerManager.getRootCARotationManager()
- .isRotationInProgress()) {
- throw new SCMException(("Root CA and Sub CA rotation is in-progress." +
- " Please try the operation later again."),
- SCMException.ResultCodes.CA_ROTATION_IN_PROGRESS);
- }
+ checkIfCertSignRequestAllowed(
+ storageContainerManager.getRootCARotationManager(), false, config,
+ "getDataNodeCertificate");
return getEncodedCertToString(certSignReq, NodeType.DATANODE);
}
@@ -208,13 +207,9 @@ public class SCMSecurityProtocolServer implements
SCMSecurityProtocol,
nodeDetails.getNodeType(), nodeDetails.getHostName(),
nodeDetails.getUuid());
Objects.requireNonNull(nodeDetails);
- if (storageContainerManager.getRootCARotationManager() != null &&
- storageContainerManager.getRootCARotationManager()
- .isRotationInProgress()) {
- throw new SCMException(("Root CA and Sub CA rotation is in-progress." +
- " Please try the operation later again."),
- SCMException.ResultCodes.CA_ROTATION_IN_PROGRESS);
- }
+ checkIfCertSignRequestAllowed(
+ storageContainerManager.getRootCARotationManager(), false, config,
+ "getCertificate");
return getEncodedCertToString(certSignReq, nodeDetails.getNodeType());
}
@@ -289,13 +284,9 @@ public class SCMSecurityProtocolServer implements
SCMSecurityProtocol,
LOGGER.info("Processing CSR for om {}, UUID: {}", omDetails.getHostName(),
omDetails.getUuid());
Objects.requireNonNull(omDetails);
- if (storageContainerManager.getRootCARotationManager() != null &&
- storageContainerManager.getRootCARotationManager()
- .isRotationInProgress()) {
- throw new SCMException(("Root CA and Sub CA rotation is in-progress." +
- " Please try the operation later again."),
- SCMException.ResultCodes.CA_ROTATION_IN_PROGRESS);
- }
+ checkIfCertSignRequestAllowed(
+ storageContainerManager.getRootCARotationManager(), false, config,
+ "getOMCertificate");
return getEncodedCertToString(certSignReq, NodeType.OM);
}
@@ -332,13 +323,9 @@ public class SCMSecurityProtocolServer implements
SCMSecurityProtocol,
+ storageContainerManager.getClusterId());
}
- if (storageContainerManager.getRootCARotationManager() != null &&
- storageContainerManager.getRootCARotationManager()
- .isRotationInProgress() && !isRenew) {
- throw new SCMException(("Root CA and Sub CA rotation is in-progress." +
- " Please try the operation later again."),
- SCMException.ResultCodes.CA_ROTATION_IN_PROGRESS);
- }
+ checkIfCertSignRequestAllowed(
+ storageContainerManager.getRootCARotationManager(), isRenew, config,
+ "getSCMCertificate");
LOGGER.info("Processing CSR for scm {}, nodeId: {}",
scmNodeDetails.getHostName(), scmNodeDetails.getScmNodeId());
diff --git
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
index 08636f1f0f..3cb4fa8754 100644
---
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
+++
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
@@ -192,6 +192,7 @@ import static
org.apache.hadoop.hdds.HddsUtils.preserveThreadName;
import static org.apache.hadoop.hdds.ratis.RatisHelper.newJvmPauseMonitor;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_EVENT_REPORT_EXEC_WAIT_THRESHOLD_DEFAULT;
import static
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_EVENT_REPORT_QUEUE_WAIT_THRESHOLD_DEFAULT;
+import static
org.apache.hadoop.hdds.scm.ScmUtils.checkIfCertSignRequestAllowed;
import static
org.apache.hadoop.hdds.scm.security.SecretKeyManagerService.isSecretKeyEnable;
import static
org.apache.hadoop.hdds.security.x509.certificate.authority.CertificateStore.CertType.VALID_CERTS;
import static org.apache.hadoop.hdds.utils.HddsServerUtil.getRemoteUser;
@@ -2144,12 +2145,8 @@ public final class StorageContainerManager extends
ServiceRuntimeInfoImpl
throw new IOException("Cannot remove current leader.");
}
- if (rootCARotationManager != null &&
- rootCARotationManager.isRotationInProgress()) {
- throw new SCMException(("Root CA and Sub CA rotation is in-progress." +
- " Please try the operation later again."),
- ResultCodes.CA_ROTATION_IN_PROGRESS);
- }
+ checkIfCertSignRequestAllowed(rootCARotationManager, false, configuration,
+ "removePeerFromHARing");
Preconditions.checkNotNull(getScmHAManager().getRatisServer()
.getDivision().getGroup());
diff --git
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/security/TestRootCARotationManager.java
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/security/TestRootCARotationManager.java
index 1294150824..db6bfdf003 100644
---
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/security/TestRootCARotationManager.java
+++
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/security/TestRootCARotationManager.java
@@ -25,11 +25,14 @@ import org.apache.hadoop.hdds.scm.ha.SCMHAManager;
import org.apache.hadoop.hdds.scm.ha.SCMRatisServerImpl;
import org.apache.hadoop.hdds.scm.ha.SCMServiceManager;
import org.apache.hadoop.hdds.scm.ha.SequenceIdGenerator;
+import org.apache.hadoop.hdds.scm.ha.StatefulServiceStateManager;
import org.apache.hadoop.hdds.scm.server.SCMSecurityProtocolServer;
import org.apache.hadoop.hdds.scm.server.SCMStorageConfig;
import org.apache.hadoop.hdds.scm.server.StorageContainerManager;
import org.apache.hadoop.hdds.security.SecurityConfig;
+import org.apache.hadoop.hdds.security.x509.certificate.CertInfo;
import
org.apache.hadoop.hdds.security.x509.certificate.client.SCMCertificateClient;
+import org.apache.hadoop.hdds.security.x509.certificate.utils.CertificateCodec;
import
org.apache.hadoop.hdds.security.x509.certificate.utils.SelfSignedCertificate;
import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
import org.apache.ozone.test.GenericTestUtils;
@@ -61,8 +64,12 @@ import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_CA_ROTATION_TIME_O
import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_EXPIRED_CERTIFICATE_CHECK_INTERVAL;
import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_GRACE_DURATION_TOKEN_CHECKS_ENABLED;
import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_RENEW_GRACE_DURATION;
+import static
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_ROOTCA_CERTIFICATE_POLLING_INTERVAL;
import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.slf4j.event.Level.INFO;
@@ -83,6 +90,7 @@ public class TestRootCARotationManager {
private SCMStorageConfig scmStorageConfig;
private SCMSecurityProtocolServer scmSecurityProtocolServer;
private RootCARotationHandlerImpl handler;
+ private StatefulServiceStateManager statefulServiceStateManager;
private File testDir;
private String cID = UUID.randomUUID().toString();
private String scmID = UUID.randomUUID().toString();
@@ -112,6 +120,8 @@ public class TestRootCARotationManager {
scmStorageConfig.setClusterId(cID);
scmSecurityProtocolServer = Mockito.mock(SCMSecurityProtocolServer.class);
handler = Mockito.mock(RootCARotationHandlerImpl.class);
+ statefulServiceStateManager =
+ Mockito.mock(StatefulServiceStateManager.class);
when(scmContext.isLeader()).thenReturn(true);
when(scm.getConfiguration()).thenReturn(ozoneConfig);
when(scm.getScmCertificateClient()).thenReturn(scmCertClient);
@@ -127,6 +137,10 @@ public class TestRootCARotationManager {
Mockito.doNothing().when(scmSecurityProtocolServer)
.setRootCertificateServer(Mockito.anyObject());
Mockito.doNothing().when(handler).rotationPrepare(Mockito.anyString());
+ when(scm.getStatefulServiceStateManager())
+ .thenReturn(statefulServiceStateManager);
+ when(statefulServiceStateManager.readConfiguration(Mockito.anyString()))
+ .thenReturn(null);
}
@AfterEach
@@ -256,6 +270,75 @@ public class TestRootCARotationManager {
100, 10000);
}
+ @Test
+ public void testPostProcessingCheck() throws Exception {
+ ozoneConfig.set(HDDS_X509_CA_ROTATION_CHECK_INTERNAL, "PT2S");
+ ozoneConfig.set(HDDS_X509_RENEW_GRACE_DURATION, "PT15S");
+ ozoneConfig.set(HDDS_X509_CA_ROTATION_ACK_TIMEOUT, "PT2S");
+ ozoneConfig.set(HDDS_X509_ROOTCA_CERTIFICATE_POLLING_INTERVAL, "PT10S");
+ Date date = Calendar.getInstance().getTime();
+ date.setMinutes(date.getMinutes() + 5);
+ ozoneConfig.set(HDDS_X509_CA_ROTATION_TIME_OF_DAY,
+ String.format("%02d", date.getHours()) + ":" +
+ String.format("%02d", date.getMinutes()) + ":" +
+ String.format("%02d", date.getSeconds()));
+
+ X509Certificate cert = generateX509Cert(ozoneConfig,
+ LocalDateTime.now(), Duration.ofSeconds(60));
+ scmCertClient.setCACertificate(cert);
+ CertificateCodec certCodec = new CertificateCodec(securityConfig,
+ "scm/sub-ca");
+ certCodec.writeCertificate(CertificateCodec.getCertificateHolder(cert));
+ rootCARotationManager = new RootCARotationManager(scm);
+ rootCARotationManager.setRootCARotationHandler(handler);
+ GenericTestUtils.LogCapturer logs =
+ GenericTestUtils.LogCapturer.captureLogs(RootCARotationManager.LOG);
+ GenericTestUtils.setLogLevel(RootCARotationManager.LOG, INFO);
+ rootCARotationManager.start();
+ rootCARotationManager.notifyStatusChanged();
+
+ GenericTestUtils.waitFor(
+ () -> logs.getOutput().contains("No RootCARotationManager " +
+ "configuration found in stateful storage"),
+ 100, 10000);
+
+ when(statefulServiceStateManager.readConfiguration(Mockito.anyString()))
+ .thenReturn(new CertInfo.Builder().setX509Certificate(cert)
+ .setTimestamp(cert.getNotBefore().getTime())
+ .build().getProtobuf().toByteString());
+
+ logs.clearOutput();
+ when(scmContext.isLeader()).thenReturn(false);
+ rootCARotationManager.notifyStatusChanged();
+ when(scmContext.isLeader()).thenReturn(true);
+ rootCARotationManager.notifyStatusChanged();
+ GenericTestUtils.waitFor(
+ () -> logs.getOutput().contains("isPostProcessing is true for"),
+ 100, 10000);
+
+ logs.clearOutput();
+ when(scmContext.isLeader()).thenReturn(false);
+ rootCARotationManager.notifyStatusChanged();
+ GenericTestUtils.waitFor(
+ () -> logs.getOutput().contains("disable monitor task"),
+ 100, 10000);
+
+ logs.clearOutput();
+ when(scmContext.isLeader()).thenReturn(true);
+ rootCARotationManager.notifyStatusChanged();
+ GenericTestUtils.waitFor(
+ () -> logs.getOutput().contains("isPostProcessing is true for"),
+ 100, 10000);
+
+ GenericTestUtils.waitFor(
+ () -> logs.getOutput().contains("isPostProcessing is false"),
+ 100, 10000);
+ doNothing().when(statefulServiceStateManager)
+ .deleteConfiguration(Mockito.anyString());
+ verify(statefulServiceStateManager, times(1))
+ .deleteConfiguration(Mockito.anyString());
+ }
+
private X509Certificate generateX509Cert(
OzoneConfiguration conf, LocalDateTime startDate,
Duration certLifetime) throws Exception {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]