Skip to content

Commit 515e652

Browse files
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 6a19e0e commit 515e652

File tree

6 files changed

+142
-5
lines changed

6 files changed

+142
-5
lines changed

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

+18
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ public class ServerConfiguration extends AbstractConfiguration<ServerConfigurati
302302
// Certificate role based authorization
303303
protected static final String AUTHORIZED_ROLES = "authorizedRoles";
304304

305+
// Is true if only Secure Client Connections are allowed.
306+
protected static final String ONLY_SECURE_CLIENTS_ALLOWED = "onlySecureClientsAllowed";
307+
305308
/**
306309
* Construct a default configuration object.
307310
*/
@@ -3557,4 +3560,19 @@ public ServerConfiguration setAuthorizedRoles(String roles) {
35573560
this.setProperty(AUTHORIZED_ROLES, roles);
35583561
return this;
35593562
}
3563+
3564+
/*
3565+
* True if only secured client connections are allowed.
3566+
*/
3567+
public boolean isOnlySecureClientConnectionAllowed() {
3568+
return this.getBoolean(ONLY_SECURE_CLIENTS_ALLOWED, false);
3569+
}
3570+
3571+
/**
3572+
* Enable/Disable the feature to allow only Secure Client Connections.
3573+
*/
3574+
public ServerConfiguration setOnlySecureClientConnectionAllowed(boolean onlySecureClientConnectionAllowed) {
3575+
this.setProperty(ONLY_SECURE_CLIENTS_ALLOWED, Boolean.toString(onlySecureClientConnectionAllowed));
3576+
return this;
3577+
}
35603578
}

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

+23-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.apache.bookkeeper.auth.BookieAuthProvider;
4646
import org.apache.bookkeeper.auth.ClientAuthProvider;
4747
import org.apache.bookkeeper.client.BKException;
48+
import org.apache.bookkeeper.conf.ServerConfiguration;
4849
import org.apache.bookkeeper.proto.BookkeeperProtocol.AuthMessage;
4950
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
5051
import org.slf4j.Logger;
@@ -58,9 +59,12 @@ static class ServerSideHandler extends ChannelInboundHandlerAdapter {
5859
volatile boolean authenticated = false;
5960
final BookieAuthProvider.Factory authProviderFactory;
6061
final BookieConnectionPeer connectionPeer;
62+
final ServerConfiguration serverCfg;
6163
BookieAuthProvider authProvider;
6264

63-
ServerSideHandler(BookieConnectionPeer connectionPeer, BookieAuthProvider.Factory authProviderFactory) {
65+
ServerSideHandler(ServerConfiguration serverCfg, BookieConnectionPeer connectionPeer,
66+
BookieAuthProvider.Factory authProviderFactory) {
67+
this.serverCfg = serverCfg;
6468
this.authProviderFactory = authProviderFactory;
6569
this.connectionPeer = connectionPeer;
6670
authProvider = null;
@@ -92,6 +96,24 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
9296
}
9397

9498
if (authenticated) {
99+
if (msg instanceof BookkeeperProtocol.Request) {
100+
Channel c = ctx.channel();
101+
BookkeeperProtocol.Request req = (BookkeeperProtocol.Request) msg;
102+
// If onlySecureClientsAllowed is set to True, Allow operations from only the Secure Clients.
103+
// The Secure Clients have the tls handler installed in their Channel Pipeline.
104+
if (serverCfg.isOnlySecureClientConnectionAllowed()
105+
&& req.getHeader().getOperation() != BookkeeperProtocol.OperationType.START_TLS
106+
&& c.pipeline().get(SslHandler.class) == null) {
107+
LOG.error("Request from Non-secure connection is not allowed",
108+
req.getHeader().getOperation());
109+
// We return Authentication Failure(EAUTH) error to Client
110+
BookkeeperProtocol.Response.Builder builder = BookkeeperProtocol.Response.newBuilder()
111+
.setHeader(req.getHeader())
112+
.setStatus(BookkeeperProtocol.StatusCode.EUA);
113+
ctx.channel().writeAndFlush(builder.build());
114+
return;
115+
}
116+
}
95117
super.channelRead(ctx, msg);
96118
} else if (msg instanceof BookieProtocol.AuthRequest) { // pre-PB-client
97119
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
@@ -337,7 +337,7 @@ protected void initChannel(SocketChannel ch) throws Exception {
337337

338338
pipeline.addLast("bookieProtoDecoder", new BookieProtoEncoding.RequestDecoder(registry));
339339
pipeline.addLast("bookieProtoEncoder", new BookieProtoEncoding.ResponseEncoder(registry));
340-
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(
340+
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(conf,
341341
contextHandler.getConnectionPeer(), authProviderFactory));
342342

343343
ChannelInboundHandler requestHandler = isRunning.get()
@@ -398,7 +398,7 @@ protected void initChannel(LocalChannel ch) throws Exception {
398398

399399
pipeline.addLast("bookieProtoDecoder", new BookieProtoEncoding.RequestDecoder(registry));
400400
pipeline.addLast("bookieProtoEncoder", new BookieProtoEncoding.ResponseEncoder(registry));
401-
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(
401+
pipeline.addLast("bookieAuthHandler", new AuthHandler.ServerSideHandler(conf,
402402
contextHandler.getConnectionPeer(), authProviderFactory));
403403

404404
ChannelInboundHandler requestHandler = isRunning.get()

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

+6
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,12 @@ public void processRequest(Object msg, Channel c) {
360360
MDC.clear();
361361
}
362362
} else {
363+
if (serverCfg.isOnlySecureClientConnectionAllowed()) {
364+
LOG.error("Client request of an older protocol version " + BookieProtocol.CURRENT_PROTOCOL_VERSION
365+
+ " denied because server config option \"onlySecureClientsAllowed=true\" and this version "
366+
+ "of the protocol does not support TLS.");
367+
return;
368+
}
363369
BookieProtocol.Request r = (BookieProtocol.Request) msg;
364370
// process packet
365371
switch (r.getOpCode()) {

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

+89-2
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,20 @@
5454
import org.apache.bookkeeper.conf.ClientConfiguration;
5555
import org.apache.bookkeeper.conf.ServerConfiguration;
5656
import org.apache.bookkeeper.net.BookieId;
57+
import org.apache.bookkeeper.net.BookieSocketAddress;
5758
import org.apache.bookkeeper.proto.BookieConnectionPeer;
5859
import org.apache.bookkeeper.proto.BookieServer;
5960
import org.apache.bookkeeper.proto.ClientConnectionPeer;
6061
import org.apache.bookkeeper.proto.TestPerChannelBookieClient;
62+
import org.apache.bookkeeper.server.conf.BookieConfiguration;
6163
import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
6264
import org.apache.bookkeeper.test.TestStatsProvider;
6365
import org.apache.bookkeeper.tls.TLSContextFactory.KeyStoreType;
6466
import org.apache.bookkeeper.util.IOUtils;
67+
import org.apache.bookkeeper.util.PortManager;
6568
import org.apache.bookkeeper.util.TestUtils;
6669
import org.apache.commons.io.FileUtils;
70+
import org.eclipse.jetty.server.Server;
6771
import org.junit.After;
6872
import org.junit.Assert;
6973
import org.junit.Before;
@@ -498,9 +502,11 @@ public void testConnectToTLSClusterTLSClientWithAuthentication() throws Exceptio
498502

499503
/**
500504
* Verify that a client without tls enabled can connect to a cluster with TLS.
505+
* In the Default Bookie config, ONLY_SECURE_CLIENTS_ALLOWED is set to false.
506+
* Therefore, the Bookie allows non-secure client connection.
501507
*/
502508
@Test
503-
public void testConnectToTLSClusterNonTLSClient() throws Exception {
509+
public void testConnectToTLSClusterNonTLSClient1() throws Exception {
504510
ClientConfiguration conf = new ClientConfiguration(baseClientConf);
505511
conf.setTLSProviderFactoryClass(null);
506512
try {
@@ -510,6 +516,87 @@ public void testConnectToTLSClusterNonTLSClient() throws Exception {
510516
}
511517
}
512518

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

conf/bk_server.conf

+4
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,10 @@ httpServerClass=org.apache.bookkeeper.http.vertx.VertxHttpServer
278278
# Tls certificate files refresh duration in seconds.
279279
# tlsCertFilesRefreshDurationSeconds=0
280280

281+
# Set true if only Secure Client Connection is allowed
282+
# onlySecureClientsAllowed=
283+
284+
281285
############################################## Bookie Storage ##############################################
282286

283287

0 commit comments

Comments
 (0)