Skip to content

Commit d3a938b

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 d3a938b

File tree

6 files changed

+139
-6
lines changed

6 files changed

+139
-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

+24-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
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;
3233
import java.io.IOException;
3334
import java.net.SocketAddress;
3435
import java.util.Queue;
@@ -39,6 +40,7 @@
3940
import org.apache.bookkeeper.auth.BookieAuthProvider;
4041
import org.apache.bookkeeper.auth.ClientAuthProvider;
4142
import org.apache.bookkeeper.client.BKException;
43+
import org.apache.bookkeeper.conf.ServerConfiguration;
4244
import org.apache.bookkeeper.proto.BookkeeperProtocol.AuthMessage;
4345
import org.apache.bookkeeper.util.ByteBufList;
4446
import org.apache.bookkeeper.util.NettyChannelUtil;
@@ -52,9 +54,12 @@ static class ServerSideHandler extends ChannelInboundHandlerAdapter {
5254
volatile boolean authenticated = false;
5355
final BookieAuthProvider.Factory authProviderFactory;
5456
final BookieConnectionPeer connectionPeer;
57+
final ServerConfiguration serverCfg;
5558
BookieAuthProvider authProvider;
5659

57-
ServerSideHandler(BookieConnectionPeer connectionPeer, BookieAuthProvider.Factory authProviderFactory) {
60+
ServerSideHandler(ServerConfiguration serverCfg, BookieConnectionPeer connectionPeer,
61+
BookieAuthProvider.Factory authProviderFactory) {
62+
this.serverCfg = serverCfg;
5863
this.authProviderFactory = authProviderFactory;
5964
this.connectionPeer = connectionPeer;
6065
authProvider = null;
@@ -86,6 +91,24 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
8691
}
8792

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