Certificate role based authorization in Apache Bookkeeper. (#2429)

This feature allows a predefined set of services to be 'whitelisted' to be able to access bookkeeper based on their client certificates. _This feature is disabled by default._


**Motivation**
As BookKeeper and its supported services move to a cloud friendly service based architecture, it becomes of utmost importance to monitor and allow only certain qualified services to be able to access the data in BK.
We have TLS based authentication, however, any service with the rootCA can access Bookkeeper clusters which is not desirable.

**Changes**

To enable if, we have to set 2 configuration options in server config.

1. Set `bookieAuthProviderFactoryClass` config option to use BookieAuthZFactory
`bookieAuthProviderFactoryClass=org.apache.bookkeeper.tls.BookieAuthZFactory`

2. Set `authorizedRoles` to a comma separated list of roles present in client certificates' OU field.
`authorizedRoles=pulsar-broker-1,pulsar-broker-2`

Read further for details on how to implement these in your client certificates and how to wire it up.

So this feature can be broken down into two parts:

    Certificate and roles
    Server configuration for authorized roles

**Details:**
_Certificate and roles:_
Here is an example of how the SUBJECT field of a final certificate for Apache Pulsar running in the cloud would look like:

    CN=apache.bookkeeper.org
    O=apache-pulsar
    OU=0:pulsar-broker-role;1:cluster-1
    L=San Francisco
    S=CA
    C=US

This shows that this bookkeeper client certificate is owned by the apache pulsar service has the role ‘pulsar-broker-role’ for entities in ‘cluster-1’.
Only those services with pulsar-broker-role should be able to access it.
We can add more fields separated by commas to increase the upstream application clusters to be able to access this bookkeeper cluster.

For example: `OU=0:herddb-readonlyNode,herddb-readwriteNode;1:herddb-cluster2`

Such separation of access based on services is paramount to keeping this secure as many upstream users of BookKeeper are financial institutions, databases and other services.

_Server configuration for authorized roles_
Once we have a certificate whose SUBJECT field has the OU attribute with the roles we want to authorize, on the Bookie side, we need to specify which roles are allowed.
We make this happen by introducing a server configuration option called `authorizedRoles`.
Since we have only static options, this will be set in stone as long as the bookie booted up with it.
If in case we need to change the allowed roles, we’ll need to stop the bookie, update the configuration file and then restart the bookie.
We can have multiple roles which are authorized as the OU field can have multiple comma separated values for roles.

This is a redo of stale PR #2355 

Master Issue: #2354
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
index 6dd2c9f..da74805 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java
@@ -290,6 +290,9 @@
     // Perform local consistency check on bookie startup
     protected static final String LOCAL_CONSISTENCY_CHECK_ON_STARTUP = "localConsistencyCheckOnStartup";
 
+    // Certificate role based authorization
+    protected static final String AUTHORIZED_ROLES = "authorizedRoles";
+
     /**
      * Construct a default configuration object.
      */
@@ -3382,4 +3385,23 @@
     public boolean isLocalConsistencyCheckOnStartup() {
         return this.getBoolean(LOCAL_CONSISTENCY_CHECK_ON_STARTUP, false);
     }
+
+    /**
+     * Get the authorized roles.
+     *
+     * @return String array of configured auth roles.
+     */
+    public String[] getAuthorizedRoles() {
+        return getStringArray(AUTHORIZED_ROLES);
+    }
+
+    /**
+     * Set authorized roles.
+     *
+     * @return Configuration Object with roles set
+     */
+    public ServerConfiguration setAuthorizedRoles(String roles) {
+        this.setProperty(AUTHORIZED_ROLES, roles);
+        return this;
+    }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/AuthHandler.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/AuthHandler.java
index ad40a88..e05846e 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/AuthHandler.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/AuthHandler.java
@@ -157,6 +157,10 @@
             return true;
         }
 
+        public boolean isAuthenticated() {
+            return authenticated;
+        }
+
         static class AuthResponseCallbackLegacy implements AuthCallbacks.GenericCallback<AuthToken> {
             final BookieProtocol.AuthRequest req;
             final Channel channel;
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieRequestProcessor.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieRequestProcessor.java
index 82f9f2a..79457aa 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieRequestProcessor.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieRequestProcessor.java
@@ -564,10 +564,19 @@
                     AuthHandler.ServerSideHandler authHandler = c.pipeline()
                             .get(AuthHandler.ServerSideHandler.class);
                     authHandler.authProvider.onProtocolUpgrade();
-                    if (future.isSuccess()) {
+
+                    /*
+                     * Success of the future doesn't guarantee success in authentication
+                     * future.isSuccess() only checks if the result field is not null
+                     */
+                    if (future.isSuccess() && authHandler.isAuthenticated()) {
                         LOG.info("Session is protected by: {}", sslHandler.engine().getSession().getCipherSuite());
                     } else {
-                        LOG.error("TLS Handshake failure.", future.cause());
+                        if (future.isSuccess()) {
+                            LOG.error("TLS Handshake failed: Could not authenticate.");
+                        } else {
+                            LOG.error("TLS Handshake failure: {} ", future.cause());
+                        }
                         BookkeeperProtocol.Response.Builder errResponse = BookkeeperProtocol.Response.newBuilder()
                                 .setHeader(r.getHeader()).setStatus(BookkeeperProtocol.StatusCode.EIO);
                         c.writeAndFlush(errResponse.build());
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/tls/BookieAuthZFactory.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tls/BookieAuthZFactory.java
new file mode 100644
index 0000000..c047977
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/tls/BookieAuthZFactory.java
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bookkeeper.tls;
+
+import com.google.common.base.Strings;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.bookkeeper.auth.AuthCallbacks;
+import org.apache.bookkeeper.auth.AuthToken;
+import org.apache.bookkeeper.auth.BookKeeperPrincipal;
+import org.apache.bookkeeper.auth.BookieAuthProvider;
+import org.apache.bookkeeper.client.BKException;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.apache.bookkeeper.proto.BookieConnectionPeer;
+import org.apache.bookkeeper.util.CertUtils;
+
+
+/**
+ * Authorization factory class.
+ */
+@Slf4j
+public class BookieAuthZFactory implements BookieAuthProvider.Factory {
+
+    public String[] allowedRoles;
+
+    @Override
+    public String getPluginName() {
+        return "BookieAuthZFactory";
+    }
+
+    @Override
+    public void init(ServerConfiguration conf) throws IOException {
+        // Read from config
+        allowedRoles = conf.getAuthorizedRoles();
+
+        if (allowedRoles == null || allowedRoles.length == 0) {
+            throw new RuntimeException("Configuration option \'bookieAuthProviderFactoryClass\' is set to"
+                    + " \'BookieAuthZFactory\' but no roles set for configuration field \'authorizedRoles\'.");
+        }
+
+        // If authorization is enabled and there are no roles, exit
+        for (String allowedRole : allowedRoles) {
+            if (Strings.isNullOrEmpty(allowedRole)) {
+                throw new RuntimeException("Configuration option \'bookieAuthProviderFactoryClass\' is set to"
+                        + " \'BookieAuthZFactory\' but no roles set for configuration field \'authorizedRoles\'.");
+            }
+        }
+    }
+
+    @Override
+    public BookieAuthProvider newProvider(BookieConnectionPeer addr,
+                                          final AuthCallbacks.GenericCallback<Void> completeCb) {
+        return new BookieAuthProvider() {
+
+            AuthCallbacks.GenericCallback<Void> completeCallback = completeCb;
+
+            @Override
+            public void onProtocolUpgrade() {
+
+                try {
+                    boolean secureBookieSideChannel = addr.isSecure();
+                    Collection<Object> certificates = addr.getProtocolPrincipals();
+                    if (secureBookieSideChannel && !certificates.isEmpty()
+                            && certificates.iterator().next() instanceof X509Certificate) {
+                        X509Certificate tempCert = (X509Certificate) certificates.iterator().next();
+                        String[] certRole = CertUtils.getRolesFromOU(tempCert);
+                        if (certRole == null || certRole.length == 0) {
+                            log.error("AuthZ failed: No cert role in OU field of certificate. Must have a role from "
+                                            + "allowedRoles list {} host: {}",
+                                    allowedRoles, addr.getRemoteAddr());
+                            completeCallback.operationComplete(BKException.Code.UnauthorizedAccessException, null);
+                            return;
+                        }
+                        boolean authorized = false;
+                        for (String allowedRole : allowedRoles) {
+                            if (certRole[0].equals(allowedRole)) {
+                                authorized = true;
+                            }
+                        }
+                        if (authorized) {
+                            addr.setAuthorizedId(new BookKeeperPrincipal(certRole[0]));
+                            completeCallback.operationComplete(BKException.Code.OK, null);
+                        } else {
+                            log.error("AuthZ failed: Cert role {} doesn't match allowedRoles list {}; host: {}",
+                                    certRole, allowedRoles, addr.getRemoteAddr());
+                            completeCallback.operationComplete(BKException.Code.UnauthorizedAccessException, null);
+                        }
+                    } else {
+                        if (!secureBookieSideChannel) {
+                            log.error("AuthZ failed: Bookie side channel is not secured; host: {}",
+                                    addr.getRemoteAddr());
+                        } else if (certificates.isEmpty()) {
+                            log.error("AuthZ failed: Certificate missing; host: {}", addr.getRemoteAddr());
+                        } else {
+                            log.error("AuthZ failed: Certs are missing or not X509 type; host: {}",
+                                    addr.getRemoteAddr());
+                        }
+                        completeCallback.operationComplete(BKException.Code.UnauthorizedAccessException, null);
+                    }
+                } catch (Exception e) {
+                    log.error("AuthZ failed: Failed to parse certificate; host: {}, {}", addr.getRemoteAddr(), e);
+                    completeCallback.operationComplete(BKException.Code.UnauthorizedAccessException, null);
+                }
+            }
+
+            @Override
+            public void process(AuthToken m, AuthCallbacks.GenericCallback<AuthToken> cb) {
+            }
+        };
+    }
+
+
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/util/CertUtils.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/util/CertUtils.java
new file mode 100644
index 0000000..e136243
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/util/CertUtils.java
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bookkeeper.util;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+/**
+ * Certificate parsing utilities.
+ */
+public abstract class CertUtils {
+
+    // OU values
+    public static final String OU_ROLE_NAME_CODE = "0";
+    public static final String OU_CLUSTER_NAME_CODE = "1";
+
+    public static final String OU_VALUES_SEPARATOR = ";";
+    public static final String OU_CODE_SEPARATOR = ":";
+    public static final String OU_NAME_SEPARATOR = ",";
+
+    static final Pattern OU_VALUES_SPLITTER = Pattern.compile(OU_VALUES_SEPARATOR);
+    static final Pattern OU_GENERAL_NAME_REGEX = Pattern.compile("^([0-9]+)" + OU_CODE_SEPARATOR + "(.*)$");
+    static final Pattern OU_NAME_SPLITTER = Pattern.compile(OU_NAME_SEPARATOR);
+
+    private CertUtils() {
+    }
+
+    public static String getOUString(X509Certificate cert) throws IOException {
+        return getOUStringFromSubject(cert.getSubjectX500Principal().getName());
+    }
+
+    public static String getOUStringFromSubject(String subject) throws IOException {
+        try {
+            LdapName ldapDN = new LdapName(subject);
+            for (Rdn rdn : ldapDN.getRdns()) {
+                if ("OU".equalsIgnoreCase(rdn.getType())) {
+                    return rdn.getValue().toString();
+                }
+            }
+            return null;
+        } catch (InvalidNameException ine) {
+            throw new IOException(ine);
+        }
+    }
+
+    public static Map<String, String> getOUMapFromOUString(String ou) throws IOException {
+        Map<String, String> ouMap = new HashMap<>();
+        if (ou != null) {
+            String[] ouParts = OU_VALUES_SPLITTER.split(ou);
+            for (String ouPart : ouParts) {
+                Matcher matcher = OU_GENERAL_NAME_REGEX.matcher(ouPart);
+                if (matcher.find() && matcher.groupCount() == 2) {
+                    ouMap.put(matcher.group(1).trim(), matcher.group(2).trim());
+                }
+            }
+        }
+        return Collections.unmodifiableMap(ouMap);
+    }
+
+    public static Map<String, String> getOUMap(X509Certificate cert) throws IOException {
+        return getOUMapFromOUString(getOUString(cert));
+    }
+
+    public static String[] getRolesFromOU(X509Certificate cert) throws IOException {
+        return getRolesFromOUMap(getOUMap(cert));
+    }
+
+    public static String[] getRolesFromOUMap(Map<String, String> ouMap) throws IOException {
+        String roleNames = ouMap.get(OU_ROLE_NAME_CODE);
+        if (roleNames != null) {
+            String[] roleParts = OU_NAME_SPLITTER.split(roleNames);
+            if (roleParts.length > 0) {
+                List<String> roles = new ArrayList<>(roleParts.length);
+                for (String role : roleParts) {
+                    roles.add(role.trim());
+                }
+                return roles.toArray(new String[roles.size()]);
+            }
+        }
+        return null;
+    }
+}
diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestBookieAuthZFactory.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestBookieAuthZFactory.java
new file mode 100644
index 0000000..046e285
--- /dev/null
+++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/tls/TestBookieAuthZFactory.java
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bookkeeper.tls;
+
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.apache.bookkeeper.auth.BookieAuthProvider;
+import org.apache.bookkeeper.common.util.ReflectionUtils;
+import org.apache.bookkeeper.conf.ServerConfiguration;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Light weight Unit Tests for BookieAuthZFactory.
+ */
+public class TestBookieAuthZFactory {
+    private static final Logger LOG = LoggerFactory.getLogger(TestBookieAuthZFactory.class);
+
+    public TestBookieAuthZFactory() {
+    }
+
+    /**
+     * Initialize a BookieAuthZFactory without configuring authorizedRoles in ServerConfiguration.
+     * This should fail as in order to use this authorization provider, we need to have authorizedRoles set.
+     */
+    @Test
+    public void testBookieAuthZInitNoRoles() {
+        ServerConfiguration conf = new ServerConfiguration();
+        String factoryClassName = BookieAuthZFactory.class.getName();
+        BookieAuthProvider.Factory factory = ReflectionUtils.newInstance(factoryClassName,
+                BookieAuthProvider.Factory.class);
+
+        try {
+            factory.init(conf);
+            fail("Not supposed to initialize BookieAuthZFactory without authorized roles set");
+        } catch (IOException | RuntimeException e) {
+            LOG.info("BookieAuthZFactory did not initialize as there are no authorized roles set.");
+        }
+    }
+
+    /**
+     * Initialize a BookieAuthZFactory as an authProvider and configure an empty string in authorizedRoles.
+     * This should fail as in order to use this as an authorization provider, we need to have valid authorizedRoles set.
+     */
+    @Test
+    public void testBookieAuthZInitEmptyRole() {
+        ServerConfiguration conf = new ServerConfiguration();
+        conf.setAuthorizedRoles("");
+        String factoryClassName = BookieAuthZFactory.class.getName();
+        BookieAuthProvider.Factory factory = ReflectionUtils.newInstance(factoryClassName,
+                BookieAuthProvider.Factory.class);
+
+        try {
+            factory.init(conf);
+            fail("Not supposed to initialize BookieAuthZFactory without authorized roles set");
+        } catch (IOException | RuntimeException e) {
+            LOG.info("BookieAuthZFactory did not initialize as there are no authorized roles set.");
+        }
+    }
+
+    /**
+     * Initialize a BookieAuthZFactory with a valid string for the configured role.
+     * However, pass a null (or faulty) connection for it to authorize, it should fail.
+     */
+    @Test
+    public void testBookieAuthZNewProviderNullAddress() {
+        ServerConfiguration conf = new ServerConfiguration();
+        conf.setAuthorizedRoles("testRole");
+        String factoryClassName = BookieAuthZFactory.class.getName();
+        BookieAuthProvider.Factory factory = ReflectionUtils.newInstance(factoryClassName,
+                BookieAuthProvider.Factory.class);
+
+        try {
+            factory.init(conf);
+            BookieAuthProvider authProvider = factory.newProvider(null, null);
+            authProvider.onProtocolUpgrade();
+            fail("BookieAuthZFactory should fail with a null connection");
+        } catch (IOException | RuntimeException e) {
+        }
+    }
+}
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 37f6e25..3123b8b 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
@@ -639,6 +639,28 @@
     }
 
     /**
+     * Verify that given role in client certificate is checked when BookieAuthZFactory is set.
+     * Positive test case where all given roles are present.
+     * If authorization fails unexpectedly, we catch the UnauthorizedAccessException and fail.
+     * Otherwise we exit the test and mark it as success
+     */
+    @Test
+    public void testRoleBasedAuthZInCertificate() throws Exception {
+        ServerConfiguration serverConf = new ServerConfiguration(baseConf);
+        serverConf.setBookieAuthProviderFactoryClass(BookieAuthZFactory.class.getCanonicalName());
+        serverConf.setAuthorizedRoles("testRole,testRole1");
+        restartBookies(serverConf);
+
+        ClientConfiguration clientConf = new ClientConfiguration(baseClientConf);
+
+        try {
+            testClient(clientConf, numBookies);
+        } catch (BKException.BKUnauthorizedAccessException bke) {
+            fail("Could not verify given role.");
+        }
+    }
+
+    /**
      * Verify that a bookie-side Auth plugin can access server certificates.
      */
     @Test
@@ -657,6 +679,10 @@
             testClient(clientConf, numBookies);
             fail("Shouldn't be able to connect");
         } catch (BKException.BKUnauthorizedAccessException authFailed) {
+        } catch (BKException.BKNotEnoughBookiesException notEnoughBookiesException) {
+            if (!useV2Protocol) {
+                fail("Unexpected exception occurred.");
+            }
         }
 
         assertTrue(secureBookieSideChannel);
@@ -685,6 +711,10 @@
             testClient(clientConf, numBookies);
             fail("Shouldn't be able to connect");
         } catch (BKException.BKUnauthorizedAccessException authFailed) {
+        } catch (BKException.BKNotEnoughBookiesException notEnoughBookiesException) {
+            if (!useV2Protocol) {
+                fail("Unexpected exception occurred.");
+            }
         }
 
         assertTrue(secureBookieSideChannel);
diff --git a/bookkeeper-server/src/test/resources/client-cert.pem b/bookkeeper-server/src/test/resources/client-cert.pem
index d819f19..c505fab 100644
--- a/bookkeeper-server/src/test/resources/client-cert.pem
+++ b/bookkeeper-server/src/test/resources/client-cert.pem
@@ -1,32 +1,35 @@
 -----BEGIN CERTIFICATE-----
-MIIFmTCCA4GgAwIBAgIJAKJZcAdMXw8CMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV
+MIIGAzCCA+ugAwIBAgIUDrW7ZpaXgcsTTWyax2tu8WqZXW0wDQYJKoZIhvcNAQEL
+BQAwgY8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZy
+YW5jaXNjbzEOMAwGA1UECgwFRHVtbXkxKzApBgNVBAsMIjA6dGVzdFJvbGUsdGVz
+dFJvbGUxOzE6dGVzdENsdXN0ZXIxHjAcBgNVBAMMFWFwYWNoZS5ib29ra2VlcGVy
+Lm9yZzAgFw0yMDA2MDMyMjIwMTlaGA8zMDE5MTAwNTIyMjAxOVowgY8xCzAJBgNV
 BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEOMAwG
-A1UECgwFRHVtbXkxHjAcBgNVBAMMFWFwYWNoZS5ib29ra2VlcGVyLm9yZzAgFw0x
-ODAxMjQxODM2MjRaGA8zMDE3MDUyNzE4MzYyNFowYjELMAkGA1UEBhMCVVMxCzAJ
-BgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ4wDAYDVQQKDAVEdW1t
-eTEeMBwGA1UEAwwVYXBhY2hlLmJvb2trZWVwZXIub3JnMIICIjANBgkqhkiG9w0B
-AQEFAAOCAg8AMIICCgKCAgEAwdphglnvMvIrEjHgBekeTtg9KmUtmK9yviI0xKdP
-8xMC6r5lvQIXQwcVB2LA7zBl3mI3s2DT9cqZ+E6q9Vz3AF9yV+F2zvPcQOt3P9hO
-CvEj9aXqMuzvSXQYmG+w7lLhm4M7IX7B4smGvXm905WACUeLr2XM1qr2SlpMZIBc
-O35NyaOeB9srQS2NXMB8mIsWDzoftbW0eNji7uz6OuyNvQWcS7rdcXBSoBRyRU2y
-qpQcfQT9aUDttDsAtkvEvuHi8LicKocaF1ufHVFvygdX7nGCNOnC83ZW5HvttwAJ
-C8Spe67etFp98bumwsXYaPO0su7M1Ym1DbphJsGO0LNwdEWstjT4uIsxyXZtblfM
-DBFpxA4s/7hIZtmrjM3CyZuoAuJWsU8UkZOVMWtxfBbY4iCo6K/ScaFSFvMykJ+p
-MST0U83JlDrqH+XjmIxcGrs0FVgD8vtaPcPJFWhm9TUBTFo1CU3Zw74W49ltgRa/
-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
+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
+q8T6QxUiKw==
 -----END CERTIFICATE-----
diff --git a/bookkeeper-server/src/test/resources/client-key.jks b/bookkeeper-server/src/test/resources/client-key.jks
index 5bd6e53..868a978 100644
--- a/bookkeeper-server/src/test/resources/client-key.jks
+++ b/bookkeeper-server/src/test/resources/client-key.jks
Binary files differ
diff --git a/bookkeeper-server/src/test/resources/client-key.p12 b/bookkeeper-server/src/test/resources/client-key.p12
index 36a9509..656f373 100644
--- a/bookkeeper-server/src/test/resources/client-key.p12
+++ b/bookkeeper-server/src/test/resources/client-key.p12
Binary files differ
diff --git a/bookkeeper-server/src/test/resources/client-key.pem b/bookkeeper-server/src/test/resources/client-key.pem
index d92a0bd..8396691 100644
--- a/bookkeeper-server/src/test/resources/client-key.pem
+++ b/bookkeeper-server/src/test/resources/client-key.pem
@@ -1,52 +1,52 @@
 -----BEGIN PRIVATE KEY-----
-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
-lZ/dii5O+DlR4aDU90na6bTa5v8ASw==
+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
+qgl5tWPWHD/K94CtNPJI6/8OdnkY3AY=
 -----END PRIVATE KEY-----
diff --git a/bookkeeper-server/src/test/resources/generateKeysAndCerts.sh b/bookkeeper-server/src/test/resources/generateKeysAndCerts.sh
index 3d8fdb6..0ce1522 100755
--- a/bookkeeper-server/src/test/resources/generateKeysAndCerts.sh
+++ b/bookkeeper-server/src/test/resources/generateKeysAndCerts.sh
@@ -61,7 +61,7 @@
         -days 365000 \
         -nodes \
         -x509 \
-        -subj "/C=US/ST=CA/L=San Francisco/O=Dummy/CN=apache.bookkeeper.org" \
+        -subj "/C=US/ST=CA/L=San Francisco/O=Dummy/OU=0:testRole,testRole1;1:testCluster/CN=apache.bookkeeper.org" \
         -out client-cert.pem \
         -keyout client-key.pem
 
diff --git a/bookkeeper-server/src/test/resources/server-cert.pem b/bookkeeper-server/src/test/resources/server-cert.pem
index b19de51..730925e 100644
--- a/bookkeeper-server/src/test/resources/server-cert.pem
+++ b/bookkeeper-server/src/test/resources/server-cert.pem
@@ -1,32 +1,33 @@
 -----BEGIN CERTIFICATE-----
-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
+MIIFpzCCA4+gAwIBAgIUfC+RpY3InsoGfZClL1JQqAueYWEwDQYJKoZIhvcNAQEL
+BQAwYjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
+bmNpc2NvMQ4wDAYDVQQKDAVEdW1teTEeMBwGA1UEAwwVYXBhY2hlLmJvb2trZWVw
+ZXIub3JnMCAXDTIwMDYwMzIyMjAxOVoYDzMwMTkxMDA1MjIyMDE5WjBiMQswCQYD
+VQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDjAM
+BgNVBAoMBUR1bW15MR4wHAYDVQQDDBVhcGFjaGUuYm9va2tlZXBlci5vcmcwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCkHmy+taLHMepkJh+pX75IYNdp
+oedKs2LEWOtLQu47JsdXH3A26Juc0aeAHK4dq+7oSGSi6MiDsi/ls4Uzz2szoti6
+iZLPx6PdUDWoCmNHktQ12heCpN/my/PytB3gneZK/CtUa+mkGpmF8MsMttGAJ/bq
+qJqsgJofIoQjYw5cD+vGvUeL9GCKt2qC6ZnonGLqcKF67kuBQdkSePAPsLYk9LQZ
+gfZH1xAqJl2oOnFKmKOSsECXyJg5m82Fj3T4RgzgmPjBsLpa9H08dYFjhqh7OasS
+mNUAzBNYgkFa8fpm4ip1JqHuLIodTLe5UjFwCoAoGeYUwww5pzdE/9Chr30azr/d
+7+NDy263/IM2Pe+lnJejad1QOkDHrmWS+VSAwQQSNQnPQwksCSib/dTJockfd3/h
+EUBU7mY3LCo75RYbjNR4VS+NN6rMggwYeWoa20C+0mvYWvRP5x4QgEtcH6TxIQuo
+B+TjUp9PiNi3M2VafUTu9OXWSorH99+nRiM7w04W2ITRU9HSBix8fSezX1vF6qov
+yDHVudX8d+6QRrfdpEWza0Bd3pj41tXFP1zIcHQVl/D/kt1KVkERKQbpXUZytElO
+HjDn9netmDi06dgaUV9qUEQKOwh+PzQB0fcsD/Ydi90dIjM53SfDj/okUrwgYcej
+sxfEi1kex5yAFdvvvwIDAQABo1MwUTAdBgNVHQ4EFgQUiaXEwotFXGBiWwazyL5j
+GjNbknYwHwYDVR0jBBgwFoAUiaXEwotFXGBiWwazyL5jGjNbknYwDwYDVR0TAQH/
+BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXGHM35Oz20KdmKR0b/J7Ig+WyUwu
+jRrVw1G9FzQatI9TwVcpNkLjJENB5OxmfqIWcdBwI+gw7inS+9zCQVvjPBMUxbIl
+MgglpIVHEZaT3O45/cTmAdBDRIx4Kl22YJJhqPA3H+y2eFG4YnU6PBYQJn2aac/h
+qOfINgz0LaAtQ+6EFt9+tTbyx9qTWjEpCRlod8RpHJHiShuZTV0OrpSYgU0IW5J/
+J7gak9V5R3si6ApQIlWKKW7UnG/MzGnNrc7saCklL4G+eTYS2w1Qzda8pkMbB3xT
+9MrWO+zk7sPlJ1VAV4jJcYX8mcLzugEugPSPRzywYfkWZnfIbtM2QeDEWOGSCtpu
+VXQiWNORdV2tdMAulI7XbcBTt1Zbcew4yDtDLEyb/OisU1I4kVF+qIFleSLVEnjs
+Syh57Vw1LfGaFRFBKaZXOfswMw0J1+iztszjo7Mncfc4q/vdIwDtitdqE6yPuC6D
+gSe2PkriEH8Ukpyi1jLDf3kh6SBJ/atO80KaPwKRxi4QxXKY7TnnqaZftcPj6/9l
+0gs73oXYcj6sF0gVq+hwUHyvaZWQcyZ/7pTYpKcUq0RBCmUgCAA1zooN1pmr+OzK
+yYvQjW76Xk+gqGSQxe4p4EcefRSGVeEmjgM9VHZ+oyFh/5V8qUBA3LtN6eBuzqkr
+elBfEEV+3m6pAmI=
 -----END CERTIFICATE-----
diff --git a/bookkeeper-server/src/test/resources/server-key.jks b/bookkeeper-server/src/test/resources/server-key.jks
index 3949b9d..1492080 100644
--- a/bookkeeper-server/src/test/resources/server-key.jks
+++ b/bookkeeper-server/src/test/resources/server-key.jks
Binary files differ
diff --git a/bookkeeper-server/src/test/resources/server-key.p12 b/bookkeeper-server/src/test/resources/server-key.p12
index ab9c8aa..9217cf4 100644
--- a/bookkeeper-server/src/test/resources/server-key.p12
+++ b/bookkeeper-server/src/test/resources/server-key.p12
Binary files differ
diff --git a/bookkeeper-server/src/test/resources/server-key.pem b/bookkeeper-server/src/test/resources/server-key.pem
index 1e2f1ec..9c60954 100644
--- a/bookkeeper-server/src/test/resources/server-key.pem
+++ b/bookkeeper-server/src/test/resources/server-key.pem
@@ -1,52 +1,52 @@
 -----BEGIN PRIVATE KEY-----
-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
-EJRVpONvEXHus+f1sCUmQYWYuhmJHA==
+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
+4Z63r2v2DLBpCwDzI6JMkUBY8zJ/
 -----END PRIVATE KEY-----
diff --git a/conf/bk_server.conf b/conf/bk_server.conf
index e585777..4081920 100755
--- a/conf/bk_server.conf
+++ b/conf/bk_server.conf
@@ -230,6 +230,12 @@
 #
 # permittedStartupUsers=
 
+# Certificate role based authorization for Bookie
+# To enable this option, bookieAuthProviderFactoryClass should be set to org.apache.bookkeeper.tls.BookieAuthZFactory
+# comma separated values of roles from the certificates OU field which we want to authorize for access
+#
+# authorizedRoles=
+
 #############################################################################
 ## TLS settings
 #############################################################################