Skip to content

Commit 8b28bc8

Browse files
GhatageAnup Ghatage
authored and
Anup Ghatage
committed
Don't allow non-secure client connections
(1) Introduced a new config option onlySecureClientsAllowed in the Bookie Configuration. Default value is considered false. (2) If onlySecureClientsAllowed is set to True, the Bookies only allow the Clients to communicate over TLS. Any requests from the Clients without TLS enabled will be rejected and Client gets the Security Exception.
1 parent 4d50a44 commit 8b28bc8

File tree

6 files changed

+140
-6
lines changed

6 files changed

+140
-6
lines changed

bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java

+18
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ public class ServerConfiguration extends AbstractConfiguration<ServerConfigurati
348348
protected static final String MAX_BATCH_READ_SIZE = "maxBatchReadSize";
349349
protected static final int DEFAULT_MAX_BATCH_READ_SIZE = 5 * 1024 * 1024; // 5MB
350350

351+
// Is true if only Secure Client Connections are allowed.
352+
protected static final String ONLY_SECURE_CLIENTS_ALLOWED = "onlySecureClientsAllowed";
353+
351354
/**
352355
* Construct a default configuration object.
353356
*/
@@ -4158,4 +4161,19 @@ private String getFilePath(String fileName) {
41584161
}
41594162
return "";
41604163
}
4164+
4165+
/*
4166+
* True if only secured client connections are allowed.
4167+
*/
4168+
public boolean isOnlySecureClientConnectionAllowed() {
4169+
return this.getBoolean(ONLY_SECURE_CLIENTS_ALLOWED, false);
4170+
}
4171+
4172+
/**
4173+
* Enable/Disable the feature to allow only Secure Client Connections.
4174+
*/
4175+
public ServerConfiguration setOnlySecureClientConnectionAllowed(boolean onlySecureClientConnectionAllowed) {
4176+
this.setProperty(ONLY_SECURE_CLIENTS_ALLOWED, Boolean.toString(onlySecureClientConnectionAllowed));
4177+
return this;
4178+
}
41614179
}

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/AuthHandler.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import io.netty.channel.ChannelHandlerContext;
3030
import io.netty.channel.ChannelInboundHandlerAdapter;
3131
import io.netty.channel.ChannelPromise;
32+
import io.netty.handler.ssl.SslHandler;
33+
3234
import java.io.IOException;
3335
import java.net.SocketAddress;
3436
import java.util.Queue;
@@ -39,6 +41,7 @@
3941
import org.apache.bookkeeper.auth.BookieAuthProvider;
4042
import org.apache.bookkeeper.auth.ClientAuthProvider;
4143
import org.apache.bookkeeper.client.BKException;
44+
import org.apache.bookkeeper.conf.ServerConfiguration;
4245
import org.apache.bookkeeper.proto.BookkeeperProtocol.AuthMessage;
4346
import org.apache.bookkeeper.util.ByteBufList;
4447
import org.apache.bookkeeper.util.NettyChannelUtil;
@@ -52,9 +55,12 @@ static class ServerSideHandler extends ChannelInboundHandlerAdapter {
5255
volatile boolean authenticated = false;
5356
final BookieAuthProvider.Factory authProviderFactory;
5457
final BookieConnectionPeer connectionPeer;
58+
final ServerConfiguration serverCfg;
5559
BookieAuthProvider authProvider;
5660

57-
ServerSideHandler(BookieConnectionPeer connectionPeer, BookieAuthProvider.Factory authProviderFactory) {
61+
ServerSideHandler(ServerConfiguration serverCfg, BookieConnectionPeer connectionPeer,
62+
BookieAuthProvider.Factory authProviderFactory) {
63+
this.serverCfg = serverCfg;
5864
this.authProviderFactory = authProviderFactory;
5965
this.connectionPeer = connectionPeer;
6066
authProvider = null;
@@ -86,6 +92,24 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
8692
}
8793

8894
if (authenticated) {
95+
if (msg instanceof BookkeeperProtocol.Request) {
96+
Channel c = ctx.channel();
97+
BookkeeperProtocol.Request req = (BookkeeperProtocol.Request) msg;
98+
// If onlySecureClientsAllowed is set to True, Allow operations from only the Secure Clients.
99+
// The Secure Clients have the tls handler installed in their Channel Pipeline.
100+
if (serverCfg.isOnlySecureClientConnectionAllowed()
101+
&& req.getHeader().getOperation() != BookkeeperProtocol.OperationType.START_TLS
102+
&& c.pipeline().get(SslHandler.class) == null) {
103+
LOG.error("Request from Non-secure connection is not allowed",
104+
req.getHeader().getOperation());
105+
// We return Authentication Failure(EAUTH) error to Client
106+
BookkeeperProtocol.Response.Builder builder = BookkeeperProtocol.Response.newBuilder()
107+
.setHeader(req.getHeader())
108+
.setStatus(BookkeeperProtocol.StatusCode.EUA);
109+
ctx.channel().writeAndFlush(builder.build());
110+
return;
111+
}
112+
}
89113
super.channelRead(ctx, msg);
90114
} else if (msg instanceof BookieProtocol.AuthRequest) { // pre-PB-client
91115
BookieProtocol.AuthRequest req = (BookieProtocol.AuthRequest) msg;

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ protected void initChannel(SocketChannel ch) throws Exception {
353353

354354
pipeline.addLast("bookieProtoDecoder", new BookieProtoEncoding.RequestDecoder(registry));
355355
pipeline.addLast("bookieProtoEncoder", new BookieProtoEncoding.ResponseEncoder(registry));
356-
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(
356+
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(conf,
357357
contextHandler.getConnectionPeer(), authProviderFactory));
358358

359359
ChannelInboundHandler requestHandler = isRunning.get()
@@ -419,7 +419,7 @@ protected void initChannel(LocalChannel ch) throws Exception {
419419

420420
pipeline.addLast("bookieProtoDecoder", new BookieProtoEncoding.RequestDecoder(registry));
421421
pipeline.addLast("bookieProtoEncoder", new BookieProtoEncoding.ResponseEncoder(registry));
422-
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(
422+
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(conf,
423423
contextHandler.getConnectionPeer(), authProviderFactory));
424424

425425
ChannelInboundHandler requestHandler = isRunning.get()

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieRequestProcessor.java

+6
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,12 @@ public void processRequest(Object msg, BookieRequestHandler requestHandler) {
370370
MDC.clear();
371371
}
372372
} else {
373+
if (serverCfg.isOnlySecureClientConnectionAllowed()) {
374+
LOG.error("Client request of an older protocol version " + BookieProtocol.CURRENT_PROTOCOL_VERSION
375+
+ " denied because server config option \"onlySecureClientsAllowed=true\" and this version "
376+
+ "of the protocol does not support TLS.");
377+
return;
378+
}
373379
BookieProtocol.Request r = (BookieProtocol.Request) msg;
374380
// process packet
375381
switch (r.getOpCode()) {

bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestTLS.java

+85-3
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public static Collection<Object[]> data() {
110110
public TestTLS(String keyStoreFormat,
111111
String trustStoreFormat,
112112
boolean useV2Protocol) {
113-
super(3);
113+
super(1);
114114
this.clientKeyStoreFormat = KeyStoreType.valueOf(keyStoreFormat);
115115
this.clientTrustStoreFormat = KeyStoreType.valueOf(trustStoreFormat);
116116
this.serverKeyStoreFormat = KeyStoreType.valueOf(keyStoreFormat);
@@ -504,9 +504,11 @@ public void testConnectToTLSClusterTLSClientWithAuthentication() throws Exceptio
504504

505505
/**
506506
* Verify that a client without tls enabled can connect to a cluster with TLS.
507+
* In the Default Bookie config, ONLY_SECURE_CLIENTS_ALLOWED is set to false.
508+
* Therefore, the Bookie allows non-secure client connection.
507509
*/
508510
@Test
509-
public void testConnectToTLSClusterNonTLSClient() throws Exception {
511+
public void testConnectToTLSClusterNonTLSClient1() throws Exception {
510512
ClientConfiguration conf = new ClientConfiguration(baseClientConf);
511513
conf.setTLSProviderFactoryClass(null);
512514
try {
@@ -516,6 +518,86 @@ public void testConnectToTLSClusterNonTLSClient() throws Exception {
516518
}
517519
}
518520

521+
/**
522+
* Verify that a client without tls enabled can NOT connect to a cluster with TLS,
523+
* if ONLY_SECURE_CLIENTS_ALLOWED is set in the Bookie config.
524+
*/
525+
@Test
526+
public void testConnectToTLSClusterNonTLSClient2() throws Exception {
527+
// Set client without TLS
528+
ClientConfiguration conf = new ClientConfiguration(baseClientConf);
529+
conf.setTLSProviderFactoryClass(null);
530+
try {
531+
// Enable the feature to only allow secure clients.
532+
restartBookies(c -> {
533+
c.setOnlySecureClientConnectionAllowed(true);
534+
return c;
535+
});
536+
testClient(conf, numBookies);
537+
fail("non tls client should not be able to connect to tls enabled bookies");
538+
} catch (BKException.BKNotEnoughBookiesException nnbe) {
539+
// Correct response.
540+
} catch (BKException.BKUnauthorizedAccessException ue) {
541+
// Correct response.
542+
}
543+
}
544+
545+
/**
546+
* Verify that the Read from the non-Secure Client throws BKSecurityException
547+
* if ONLY_SECURE_CLIENTS_ALLOWED is set in the Bookie config.
548+
*/
549+
@Test
550+
public void testReadwithNonTLSBookie() throws Exception {
551+
552+
/* TestTLS tests for V3 and V2 protocols together
553+
* We should be able to create a ledger when its v3 and setOnlySecureClientConnectionAllowed(true)
554+
* We shouldn't be able to do any create / read when its v2 and setOnlySecureClientConnectionAllowed(true)
555+
* since v2 does not support TLS.
556+
*/
557+
if (useV2Protocol) {
558+
return;
559+
}
560+
561+
// Enable the feature to only allow secure clients.
562+
ClientConfiguration conf = new ClientConfiguration(baseClientConf);
563+
long lid = 0;
564+
try {
565+
restartBookies(c -> {
566+
c.setOnlySecureClientConnectionAllowed(true);
567+
return c;
568+
});
569+
// Create the Ledger
570+
BookKeeper client = new BookKeeper(conf);
571+
byte[] passwd = "testPassword".getBytes();
572+
int numEntries = 10;
573+
byte[] testEntry = "testEntry".getBytes();
574+
try (LedgerHandle lh = client.createLedger(numBookies, numBookies, DigestType.CRC32, passwd);) {
575+
for (int i = 0; i <= numEntries; i++) {
576+
lh.addEntry(testEntry);
577+
}
578+
lid = lh.getId();
579+
}
580+
} catch (BKException.BKNotEnoughBookiesException nnbe) {
581+
fail("tls client could not create the ledger");
582+
}
583+
// nonTLS client should not be able to read the ledger
584+
conf.setTLSProviderFactoryClass(null);
585+
try {
586+
BookKeeper client = new BookKeeper(conf);
587+
byte[] passwd = "testPassword".getBytes();
588+
int numEntries = 10;
589+
byte[] testEntry = "testEntry".getBytes();
590+
try (LedgerHandle lh = client.openLedger(lid, DigestType.CRC32, passwd);) {
591+
Enumeration<LedgerEntry> entries = lh.readEntries(0, numEntries);
592+
fail("The Read should've failed with BKSecurityException");
593+
}
594+
} catch (BKException.BKSecurityException se) {
595+
// Correct Response.
596+
} catch (BKException.BKUnauthorizedAccessException ue) {
597+
// Correct response.
598+
}
599+
}
600+
519601
/**
520602
* Verify that a client will fail to connect to a server if it has asked for TLS, but it is not available.
521603
*/
@@ -540,7 +622,7 @@ public void testClientWantsTLSNoServersHaveIt() throws Exception {
540622
* bookies with TLS enabled in the cluster, although few bookies do not have TLS enabled.
541623
*/
542624
@Test
543-
public void testTLSClientButOnlyFewTLSServers() throws Exception {
625+
public void testTLSClientButOnlyFewTLSServers1() throws Exception {
544626
// disable TLS on initial set of bookies
545627
restartBookies(c -> {
546628
c.setTLSProviderFactoryClass(null);

conf/bk_server.conf

+4
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ httpServerTrustStorePassword=
300300
# Tls certificate files refresh duration in seconds.
301301
# tlsCertFilesRefreshDurationSeconds=0
302302

303+
# Set true if only Secure Client Connection is allowed
304+
# onlySecureClientsAllowed=
305+
306+
303307
############################################## Bookie Storage ##############################################
304308

305309

0 commit comments

Comments
 (0)