This is an automated email from the ASF dual-hosted git repository.

sijie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new 40c8919  ISSUE #1109: Error out pending ops on TLS key mismatch 
exception
40c8919 is described below

commit 40c89198af603d94815711da40698399f8f16c75
Author: Kishore Udayashankar <[email protected]>
AuthorDate: Sat Feb 17 01:04:02 2018 +0800

    ISSUE #1109: Error out pending ops on TLS key mismatch exception
    
    Descriptions of the changes in this PR:
    
    (W-4479117)
    
    - Add Testcase to test certificate mismatch scenario
    - On tls handshake failure, replace future.get() with channel.
    - Print a warning message on exception caught in a netty context
    on connection close, drain pendingOps
    
    rev charan
    
    Master Issue: #1109
    
    Author: Kishore Udayashankar <[email protected]>
    
    Reviewers: Enrico Olivelli <[email protected]>, Sijie Guo 
<[email protected]>
    
    This closes #1110 from kishorekasi/tls-keymismatch, closes #1109
---
 .../bookkeeper/proto/PerChannelBookieClient.java   | 36 +++++++----
 .../java/org/apache/bookkeeper/tls/TestTLS.java    | 70 +++++++++++++++++-----
 2 files changed, 80 insertions(+), 26 deletions(-)

diff --git 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java
 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java
index a486d57..75b3193 100644
--- 
a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java
+++ 
b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java
@@ -938,6 +938,23 @@ public class PerChannelBookieClient extends 
ChannelInboundHandlerAdapter {
     }
 
     /**
+     * Errors out pending ops from per channel bookie client. As the channel
+     * is being closed, all the operations waiting on the connection
+     * will be sent to completion with error
+     */
+    void errorOutPendingOps(int rc) {
+        Queue<GenericCallback<PerChannelBookieClient>> oldPendingOps;
+        synchronized (this) {
+            oldPendingOps = pendingOps;
+            pendingOps = new ArrayDeque<>();
+        }
+
+        for (GenericCallback<PerChannelBookieClient> pendingOp : 
oldPendingOps) {
+            pendingOp.operationComplete(rc, PerChannelBookieClient.this);
+        }
+    }
+
+    /**
      * Errors out pending entries. We call this method from one thread to avoid
      * concurrent executions to QuorumOpMonitor (implements callbacks). It 
seems
      * simpler to call it from BookieHandle instead of calling directly from
@@ -978,6 +995,7 @@ public class PerChannelBookieClient extends 
ChannelInboundHandlerAdapter {
         }
 
         
errorOutOutstandingEntries(BKException.Code.BookieHandleNotAvailableException);
+        errorOutPendingOps(BKException.Code.BookieHandleNotAvailableException);
 
         synchronized (this) {
             if (this.channel == ctx.channel()
@@ -1014,9 +1032,7 @@ public class PerChannelBookieClient extends 
ChannelInboundHandlerAdapter {
         }
 
         if (cause instanceof IOException) {
-            // these are thrown when a bookie fails, logging them just pollutes
-            // the logs (the failure is logged from the listeners on the write
-            // operation), so I'll just ignore it here.
+            LOG.warn("Exception caught on:{} cause:", ctx.channel(), cause);
             ctx.close();
             return;
         }
@@ -1225,7 +1241,7 @@ public class PerChannelBookieClient extends 
ChannelInboundHandlerAdapter {
                         if (future.isSuccess() && state == 
ConnectionState.CONNECTING) {
                             LOG.error("Connection state changed before TLS 
handshake completed {}/{}", addr, state);
                             rc = 
BKException.Code.BookieHandleNotAvailableException;
-                            closeChannel(future.get());
+                            closeChannel(channel);
                             channel = null;
                             if (state != ConnectionState.CLOSED) {
                                 state = ConnectionState.DISCONNECTED;
@@ -1241,20 +1257,20 @@ public class PerChannelBookieClient extends 
ChannelInboundHandlerAdapter {
                         } else if (future.isSuccess()
                                 && (state == ConnectionState.CLOSED || state 
== ConnectionState.DISCONNECTED)) {
                             LOG.warn("Closed before TLS handshake completed, 
clean up: {}, current state {}",
-                                    future.get(), state);
-                            closeChannel(future.get());
+                                    channel, state);
+                            closeChannel(channel);
                             rc = 
BKException.Code.BookieHandleNotAvailableException;
                             channel = null;
                         } else if (future.isSuccess() && state == 
ConnectionState.CONNECTED) {
                             LOG.debug("Already connected with another 
channel({}), so close the new channel({})",
-                                    channel, future.get());
-                            closeChannel(future.get());
+                                    channel, channel);
+                            closeChannel(channel);
                             return; // pendingOps should have been completed 
when other channel connected
                         } else {
                             LOG.error("TLS handshake failed with bookie: 
{}/{}, current state {} : ",
-                                    future.get(), addr, state, future.cause());
+                                    channel, addr, state, future.cause());
                             rc = BKException.Code.SecurityException;
-                            closeChannel(future.get());
+                            closeChannel(channel);
                             channel = null;
                             if (state != ConnectionState.CLOSED) {
                                 state = ConnectionState.DISCONNECTED;
diff --git 
a/bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestTLS.java 
b/bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestTLS.java
index 0f51dcf..31ea7bd 100644
--- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestTLS.java
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestTLS.java
@@ -52,7 +52,9 @@ import org.apache.bookkeeper.proto.BookieConnectionPeer;
 import org.apache.bookkeeper.proto.ClientConnectionPeer;
 import org.apache.bookkeeper.proto.TestPerChannelBookieClient;
 import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
+import org.apache.bookkeeper.tls.TLSContextFactory.KeyStoreType;
 import org.junit.After;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -75,28 +77,28 @@ public class TestTLS extends BookKeeperClusterTestCase {
     private static boolean secureBookieSideChannel = false;
     private static Collection<Object> secureBookieSideChannelPrincipals = null;
 
-    private TLSContextFactory.KeyStoreType clientKeyStoreFormat;
-    private TLSContextFactory.KeyStoreType clientTrustStoreFormat;
-    private TLSContextFactory.KeyStoreType serverKeyStoreFormat;
-    private TLSContextFactory.KeyStoreType serverTrustStoreFormat;
+    private KeyStoreType clientKeyStoreFormat;
+    private KeyStoreType clientTrustStoreFormat;
+    private KeyStoreType serverKeyStoreFormat;
+    private KeyStoreType serverTrustStoreFormat;
 
     @Parameters
     public static Collection<Object[]> data() {
         return Arrays.asList(new Object[][] {
-                { "JKS", "JKS" },
-                { "PEM", "PEM" },
-                { "PKCS12", "PKCS12" },
-                { "JKS", "PEM" },
-                { "PEM", "PKCS12" },
-                { "PKCS12", "JKS" }
+                 { "JKS", "JKS" },
+                 { "PEM", "PEM" },
+                 { "PKCS12", "PKCS12" },
+                 { "JKS", "PEM" },
+                 { "PEM", "PKCS12" },
+                 { "PKCS12", "JKS" }
             });
     }
     public TestTLS(String keyStoreFormat, String trustStoreFormat) {
         super(3);
-        this.clientKeyStoreFormat = 
TLSContextFactory.KeyStoreType.valueOf(keyStoreFormat);
-        this.clientTrustStoreFormat = 
TLSContextFactory.KeyStoreType.valueOf(trustStoreFormat);
-        this.serverKeyStoreFormat = 
TLSContextFactory.KeyStoreType.valueOf(keyStoreFormat);
-        this.serverTrustStoreFormat = 
TLSContextFactory.KeyStoreType.valueOf(trustStoreFormat);
+        this.clientKeyStoreFormat = KeyStoreType.valueOf(keyStoreFormat);
+        this.clientTrustStoreFormat = KeyStoreType.valueOf(trustStoreFormat);
+        this.serverKeyStoreFormat = KeyStoreType.valueOf(keyStoreFormat);
+        this.serverTrustStoreFormat = KeyStoreType.valueOf(trustStoreFormat);
     }
 
     private String getResourcePath(String resource) throws Exception {
@@ -217,7 +219,7 @@ public class TestTLS extends BookKeeperClusterTestCase {
     /**
      * Verify that a server will not start if tls is enabled but no cert is 
specified.
      */
-    @Test(timeout = 60000)
+    @Test
     public void testStartTLSServerNoKeyStore() throws Exception {
         ServerConfiguration bookieConf = 
newServerConfiguration().setTLSKeyStore(null);
 
@@ -230,7 +232,43 @@ public class TestTLS extends BookKeeperClusterTestCase {
     }
 
     /**
-     * Verify that a server will not start if tls is enabled but the cert 
password is incorrect.
+     * Verify handshake failure with a bad cert.
+     */
+    @Test
+    public void testKeyMismatchFailure() throws Exception {
+        // Valid test case only for PEM format keys
+        Assume.assumeTrue(serverKeyStoreFormat == KeyStoreType.PEM);
+
+        ClientConfiguration clientConf = new 
ClientConfiguration(baseClientConf);
+
+        // restart a bookie with bad cert
+        int restartBookieIdx = 0;
+        ServerConfiguration bookieConf = bsConfs.get(restartBookieIdx)
+                .setTLSCertificatePath(getResourcePath("client-cert.pem"));
+        killBookie(restartBookieIdx);
+        LOG.info("Sleeping for 1s before restarting bookie with bad cert");
+        Thread.sleep(1000);
+        bs.add(startBookie(bookieConf));
+        bsConfs.add(bookieConf);
+
+        // Create ledger and write entries
+        BookKeeper client = new BookKeeper(clientConf);
+        byte[] passwd = "testPassword".getBytes();
+        int numEntries = 2;
+        byte[] testEntry = "testEntry".getBytes();
+
+        try (LedgerHandle lh = client.createLedger(numBookies, numBookies, 
DigestType.CRC32, passwd)) {
+            for (int i = 0; i <= numEntries; i++) {
+                lh.addEntry(testEntry);
+            }
+            fail("Should have failed with not enough bookies to write");
+        } catch (BKException.BKNotEnoughBookiesException bke) {
+            // expected
+        }
+    }
+
+    /**
+     * Verify that a server will not start if ssl is enabled but the cert 
password is incorrect.
      */
     @Test
     public void testStartTLSServerBadPassword() throws Exception {

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to