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 aff7f87429 HDDS-9010. Fix RootCaRotationPoller not toggling rotation 
if previous rotation failed (#5060)
aff7f87429 is described below

commit aff7f8742939c3bb13365c7b1a132dd74a3ffbaa
Author: Galsza <[email protected]>
AuthorDate: Tue Jul 18 14:19:04 2023 +0200

    HDDS-9010. Fix RootCaRotationPoller not toggling rotation if previous 
rotation failed (#5060)
---
 .../client/DefaultCertificateClient.java           | 14 +++++---
 .../certificate/client/RootCaRotationPoller.java   | 18 ++++++++--
 .../utils/TestRootCaRotationPoller.java            | 40 ++++++++++++++++++++++
 3 files changed, 66 insertions(+), 6 deletions(-)

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 e066efef8c..6077895817 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
@@ -205,7 +205,7 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
   }
 
   @Override
-  public void registerRootCARotationListener(
+  public synchronized void registerRootCARotationListener(
       Function<List<X509Certificate>, CompletableFuture<Void>> listener) {
     if (securityConfig.isAutoCARotationEnabled()) {
       rootCaRotationPoller.addRootCARotationProcessor(listener);
@@ -1333,7 +1333,8 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
       return CompletableFuture.completedFuture(null);
     }
     CertificateRenewerService renewerService =
-        new CertificateRenewerService(true);
+        new CertificateRenewerService(
+            true, rootCaRotationPoller::setCertificateRenewalError);
     return CompletableFuture.runAsync(renewerService, executorService);
   }
 
@@ -1355,7 +1356,8 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
               .setDaemon(true).build());
     }
     this.executorService.scheduleAtFixedRate(
-        new CertificateRenewerService(false),
+        new CertificateRenewerService(false, () -> {
+        }),
         timeBeforeGracePeriod, interval, TimeUnit.MILLISECONDS);
     getLogger().info("CertificateRenewerService for {} is started with " +
             "first delay {} ms and interval {} ms.", component,
@@ -1367,9 +1369,12 @@ public abstract class DefaultCertificateClient 
implements CertificateClient {
    */
   public class CertificateRenewerService implements Runnable {
     private boolean forceRenewal;
+    private Runnable rotationErrorCallback;
 
-    public CertificateRenewerService(boolean forceRenewal) {
+    public CertificateRenewerService(boolean forceRenewal,
+        Runnable rotationErrorCallback) {
       this.forceRenewal = forceRenewal;
+      this.rotationErrorCallback = rotationErrorCallback;
     }
 
     @Override
@@ -1397,6 +1402,7 @@ public abstract class DefaultCertificateClient implements 
CertificateClient {
               timeLeft, forceRenewal);
           newCertId = renewAndStoreKeyAndCertificate(forceRenewal);
         } catch (CertificateException e) {
+          rotationErrorCallback.run();
           if (e.errorCode() ==
               CertificateException.ErrorCode.ROLLBACK_ERROR) {
             if (shutdownCallback != null) {
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/RootCaRotationPoller.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/RootCaRotationPoller.java
index 47cc368bbe..2656eef92d 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/RootCaRotationPoller.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/security/x509/certificate/client/RootCaRotationPoller.java
@@ -39,6 +39,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -55,6 +56,7 @@ public class RootCaRotationPoller implements Runnable, 
Closeable {
   private final Duration pollingInterval;
   private Set<X509Certificate> knownRootCerts;
   private final SCMSecurityProtocolClientSideTranslatorPB scmSecureClient;
+  private final AtomicBoolean certificateRenewalError;
 
   public RootCaRotationPoller(SecurityConfig securityConfig,
       Set<X509Certificate> initiallyKnownRootCaCerts,
@@ -67,6 +69,7 @@ public class RootCaRotationPoller implements Runnable, 
Closeable {
             .setDaemon(true).build());
     pollingInterval = securityConfig.getRootCaCertificatePollingInterval();
     rootCARotationProcessors = new ArrayList<>();
+    certificateRenewalError = new AtomicBoolean(false);
   }
 
   private void pollRootCas() {
@@ -86,15 +89,22 @@ public class RootCaRotationPoller implements Runnable, 
Closeable {
           getPrintableCertIds(knownRootCerts) + ". Root CA Cert ids from " +
           "SCM not known by the client: " +
           getPrintableCertIds(scmCertsWithoutKnownCerts));
-
+      certificateRenewalError.set(false);
       CompletableFuture<Void> allRootCAProcessorFutures =
           CompletableFuture.allOf(rootCARotationProcessors.stream()
               .map(c -> c.apply(rootCAsFromSCM))
               .toArray(CompletableFuture[]::new));
 
       allRootCAProcessorFutures.whenComplete((unused, throwable) -> {
-        if (throwable == null) {
+        if (throwable == null && !certificateRenewalError.get()) {
           knownRootCerts = new HashSet<>(rootCAsFromSCM);
+        } else {
+          LOG.info("Certificate consumption was unsuccesfull. " +
+              (certificateRenewalError.get() ?
+                  "There was a caught exception when trying to sign the " +
+                      "certificate" :
+                  "There was an unexpected error during cert rotation" +
+                      throwable));
         }
       });
     } catch (IOException e) {
@@ -134,6 +144,10 @@ public class RootCaRotationPoller implements Runnable, 
Closeable {
     }
   }
 
+  public void setCertificateRenewalError() {
+    certificateRenewalError.set(true);
+  }
+
   private String getPrintableCertIds(Collection<X509Certificate> certs) {
     return certs.stream()
         .map(X509Certificate::getSerialNumber)
diff --git 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/utils/TestRootCaRotationPoller.java
 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/utils/TestRootCaRotationPoller.java
index 7f2ed0a813..90ac9daac5 100644
--- 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/utils/TestRootCaRotationPoller.java
+++ 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/security/x509/certificate/utils/TestRootCaRotationPoller.java
@@ -42,6 +42,7 @@ import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static 
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_ROOTCA_CERTIFICATE_POLLING_INTERVAL;
 
@@ -51,6 +52,7 @@ import static 
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_X509_ROOTCA_CERTIFICATE
 public class TestRootCaRotationPoller {
 
   private SecurityConfig secConf;
+  private GenericTestUtils.LogCapturer logCapturer;
 
   @Mock
   private SCMSecurityProtocolClientSideTranslatorPB scmSecurityClient;
@@ -61,6 +63,8 @@ public class TestRootCaRotationPoller {
     OzoneConfiguration conf = new OzoneConfiguration();
     conf.set(HDDS_X509_ROOTCA_CERTIFICATE_POLLING_INTERVAL, "PT1s");
     secConf = new SecurityConfig(conf);
+    logCapturer = GenericTestUtils.LogCapturer.captureLogs(
+        org.slf4j.LoggerFactory.getLogger(RootCaRotationPoller.class));
   }
 
   @Test
@@ -116,6 +120,42 @@ public class TestRootCaRotationPoller {
     GenericTestUtils.waitFor(atomicBoolean::get, 50, 5000);
   }
 
+  @Test
+  public void testPollerRetriesAfterFailure() throws Exception {
+    X509Certificate knownCert = generateX509Cert(
+        LocalDateTime.now(), Duration.ofSeconds(50));
+    X509Certificate newRootCa = generateX509Cert(
+        LocalDateTime.now(), Duration.ofSeconds(50));
+    HashSet<X509Certificate> knownCerts = new HashSet<>();
+    knownCerts.add(knownCert);
+    List<String> certsFromScm = new ArrayList<>();
+    certsFromScm.add(CertificateCodec.getPEMEncodedString(knownCert));
+    certsFromScm.add(CertificateCodec.getPEMEncodedString(newRootCa));
+    RootCaRotationPoller poller = new RootCaRotationPoller(secConf,
+        knownCerts, scmSecurityClient);
+    poller.run();
+    Mockito.when(scmSecurityClient.getAllRootCaCertificates())
+        .thenReturn(certsFromScm);
+    AtomicBoolean atomicBoolean = new AtomicBoolean();
+    atomicBoolean.set(false);
+    //Set that the first certificate renewal encountered an error
+    AtomicInteger runNumber = new AtomicInteger(1);
+    poller.addRootCARotationProcessor(
+        certificates -> CompletableFuture.supplyAsync(() -> {
+          if (runNumber.getAndIncrement() < 2) {
+            poller.setCertificateRenewalError();
+          }
+          atomicBoolean.set(true);
+          Assertions.assertEquals(certificates.size(), 2);
+          return null;
+        }));
+    //Assert for the poller success even if there was an error at first
+    GenericTestUtils.waitFor(atomicBoolean::get, 50, 5000);
+    //And that we see the error in the logs
+    Assertions.assertTrue(logCapturer.getOutput().contains(
+        "There was a caught exception when trying to sign the certificate"));
+  }
+
   private X509Certificate generateX509Cert(
       LocalDateTime startDate, Duration certLifetime) throws Exception {
     KeyPair keyPair = KeyStoreTestUtil.generateKeyPair("RSA");


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to