Skip to main content

Zookeeper, Netflix Curator and ACLs

If you have one or more Zookeeper "multi-tenant" clusters you may want to protect znodes against unwanted modifications.
Here is a very simple and short introduction to the ACL and custom authentication features.
This post is not intended to give you best practices about security and Zookeeper, the only goal is to give you a complete example of a custom authentication handler.

Complete source code with JUnit test is available here :
https://github.com/barkbay/zookeeper-acl-sample/

Use case

Let say that your Zookeeper cluster is used by several users. In order to restrict user actions you have decided that each user must prefix all paths with the first letter of his name.
  • User foo is only allowed to create, read, delete and update znodes under the /f znode.
  • User bar is only allowed to create, read, delete and update znodes under the /b znode.

Get client authentication data on the server side

Zookeeper client authentication can be easily customized , all you have to do is to create a class that extends org.apache.zookeeper.server.auth.AuthenticationProvider :
public class CustomUserAuthenticationProvider
implements AuthenticationProvider {
@Override
public String getScheme() {
// The name of the scheme as it is provided by client
return "user";
}
@Override
public Code handleAuthentication(ServerCnxn cnxn, byte[] authData) {
final String userName = new String(authData, Charsets.UTF_8);
// A non null or empty user name must be provided
if (!Strings.isNullOrEmpty(userName)) {
// This line is VERY important ! return code
// is not enough !
cnxn.addAuthInfo(new Id(getScheme(), userName));
return Code.OK;
}
return Code.AUTHFAILED;
}
@Override
public boolean matches(String id, String aclExpr) {
if (Strings.isNullOrEmpty(id) || Strings.isNullOrEmpty(aclExpr)) {
return false;
}
// Check if the first letter of the user name
// match the one in the acl
return id.charAt(0) == aclExpr.charAt(0);
}
@Override
public boolean isValid(String id) {
// A valid user name is at least 1 char length
return !Strings.isNullOrEmpty(id) && id.length() == 1;
}
}
Once it is done you must tell Zookeeper server to use it, this can be done by setting the Java system property zookeeper.authProvider.x (where x is an integer)
You can do this in a startup script but we want to use a JUnit test :
public class BaseClassForSecurityTests {
@Before
public void setup() throws Exception {
// Start a Zookeeper unit test server
System.setProperty(DebugUtils.PROPERTY_DONT_LOG_CONNECTION_ISSUES, "true");
// Register our custom authentication provider
System.setProperty("zookeeper.authProvider.1",
"morello.zk.acl.CustomUserAuthenticationProvider");
// Ok, create a testing Zookeeper server with Curator
server = new TestingServer();
}
@After
public void teardown() throws Exception {
System.clearProperty("zookeeeper.authProvider.1");
server.close();
}
}

Let Zookeeper client automatically set ACLs with Curator

Curator helps you to automatically set right ACLs on znodes. This can be done providing an ACLProvider implementation :
public class ACLProvider implements
com.netflix.curator.framework.api.ACLProvider {
@Override
public List<ACL> getAclForPath(String path) {
// The first letter of the path is the ACL id
final String firstLetter = String.valueOf(path.charAt(1));
final Id FIRST_USER_LETTER = new Id("user", firstLetter);
// Create a new ACL with the first letter of the path as
// an ID and give all permissions for users
ACL acl = new ACL(Perms.ALL, FIRST_USER_LETTER);
return Collections.singletonList(acl);
}
}
Register you ACL provider using the Curator client builder :
protected CuratorFrameworkFactory.Builder getClientBuilder() {
// Create a client builder
CuratorFrameworkFactory.Builder clientBuilder =
CuratorFrameworkFactory.builder()
.connectString(server.getConnectString())
.retryPolicy(new RetryOneTime(2000))
.aclProvider(new ACLProvider());
return clientBuilder;
}

Add client authentication data using the previous builder :
CuratorFramework client = getClientBuilder().authorization("user",
"myusername".getBytes(Charsets.UTF_8)).build();
Complete JUnit test with a Zookeeper testing server using the Curator framework is available here

Comments

Popular posts from this blog

Orientée colonnes ?

Les bases NoSQL sont arrivées avec leur cortège de nouveautés et pour certaines d'entre elles une notion héritée de BigTable : celle de base de donnée orientée colonne. Cependant faire le lien entre l'article de Wikipedia et comprendre ce que permet réellement un base de donnée comme HBase n'est pas une chose évidente. En effet le simple fait de définir cette notion ne suffit pas toujours a bien comprendre quels sont les principes de conception du monde SQL qui peuvent être oubliés et ceux qui doivent être appris. Colonne or not colonne ? Prenons un modèle très simple de donnée et essayons de le transposer dans un modèle "orienté colonne": Comme on peut le voir on est passé d'un modèle à 2 dimensions (ligne x colonne) vers un modèle où une valeur est accédée au travers de 2  coordonnées qui sont ici (ligne, colonne) Cette notion de coordonnées est  importante  (c'est pour ça que je la met en gras 2 fois de suite) si l'on veut c...

HBase + Subversion + Eclipse + Windows

HBase + Subversion + Eclipse + Windows (it should be easy to adapt for Linux) Update : please note that since HBase-4336 / HBase 0.96 the source tree is split in more than one Maven module this post is no more relevant, i have created a new post on this subject : http://michaelmorello.blogspot.fr/2012/06/hbase-096-eclipse-maven.html This is a simple setup in order to play with the source code of HBase under Microsoft Windows. Since HBase use some Unix specific commands like chmod the only requirements here are  Cygwin and a working Maven 3 environment. (It is obvious that you need Java and Eclipse , but you DON'T need anything else like the Eclipse Maven plugin or any SSH configuration) 1. Checkout the source code The first step is to check out the source code from the Subversion repository. I did it under my cygwin home repository. In this example i want to play with the 0.90 branch : svn co http://svn.apache.org/repos/asf/hbase/branches/0.90/ hbase-...

Row Count : HBase Aggregation example

With the coprocessors HBase 0.92 introduces a new way to process data directly on a region server. As a user this is definitively a very exciting feature : now you can easily define your own distributed data services. This post is not intended to help you how to define them (i highly recommend you to watch this presentation if you want to do so) but to quickly presents the new aggregation service shipped with HBase 0.92 that is built upon the endpoint coprocessor framework. 1. Enable AggregationClient coprocessor You have two choices : You can enable aggregation coprocessor on all your tables by adding the following lines to hbase-site.xml : <property> <name>hbase.coprocessor.user.region.classes</name> <value>org.apache.hadoop.hbase.coprocessor.AggregateImplementation</value> </property> or ...you can enable coprocessor only on a table throught the HBase shell : 1. disable the table hbase> disable ' mytable ' 2....