diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java index 570c203..dc3ceb2 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java @@ -33,6 +33,7 @@ public final class VisibilityConstants { /** Internal storage table for visibility labels */ public static final TableName LABELS_TABLE_NAME = TableName.valueOf( NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "labels"); + public static final byte[] LABELS_GLOBAL_NAME = LABELS_TABLE_NAME.getName(); /** Family for the internal storage table for visibility labels */ public static final byte[] LABELS_TABLE_FAMILY = Bytes.toBytes("f"); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java index 7e9299a..34baa5d 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java @@ -100,6 +100,7 @@ import org.apache.hadoop.hbase.security.Superusers; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.UserProvider; import org.apache.hadoop.hbase.security.access.Permission.Action; +import org.apache.hadoop.hbase.security.visibility.VisibilityConstants; import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; import org.apache.hadoop.hbase.util.ByteRange; import org.apache.hadoop.hbase.util.Bytes; @@ -1035,6 +1036,9 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preDeleteTable(ObserverContext c, TableName tableName) throws IOException { + if (isSecurityMetaTable(tableName.getName())) { + throw createSecurityMetaConstraintsViolatedException(tableName); + } requirePermission("deleteTable", tableName, null, null, Action.ADMIN, Action.CREATE); } @@ -1089,9 +1093,39 @@ public class AccessController extends BaseMasterAndRegionObserver }); } + private boolean isSecurityMetaTable(byte[] tableName) { + return (Bytes.equals(tableName, AccessControlLists.ACL_GLOBAL_NAME) + || Bytes.equals(tableName, VisibilityConstants.LABELS_GLOBAL_NAME)); + } + + private byte[] getReservedSecurityMetaColumn(byte[] tableName) { + if (Bytes.equals(tableName, AccessControlLists.ACL_GLOBAL_NAME)) + return AccessControlLists.ACL_LIST_FAMILY; + + if (Bytes.equals(tableName, VisibilityConstants.LABELS_GLOBAL_NAME)) + return VisibilityConstants.LABELS_TABLE_FAMILY; + + return null; + } + + private AccessDeniedException createSecurityMetaConstraintsViolatedException( + TableName tableName) { + return new AccessDeniedException("Not allowed to modify " + tableName + + " table with AccessController/VisibilityController coprocessors installed"); + } + + private AccessDeniedException createSecurityMetaConstraintsViolatedException(TableName tableName, + String columnFamily) { + return new AccessDeniedException("Not allowed to modify " + tableName.getNameAsString() + + ", family:" + columnFamily + " table with AccessController/VisibilityController installed"); + } + @Override public void preModifyTable(ObserverContext c, TableName tableName, HTableDescriptor htd) throws IOException { + if (isSecurityMetaTable(tableName.getName())) { + checkSystemOrSuperUser(); + } requirePermission("modifyTable", tableName, null, null, Action.ADMIN, Action.CREATE); } @@ -1115,23 +1149,34 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preAddColumnFamily(ObserverContext ctx, - TableName tableName, HColumnDescriptor columnFamily) - throws IOException { + TableName tableName, HColumnDescriptor columnFamily) throws IOException { + if (isSecurityMetaTable(tableName.getName())) { + checkSystemOrSuperUser(); + } requireTablePermission("addColumn", tableName, columnFamily.getName(), null, Action.ADMIN, - Action.CREATE); + Action.CREATE); } @Override public void preModifyColumnFamily(ObserverContext ctx, TableName tableName, HColumnDescriptor columnFamily) throws IOException { + if (isSecurityMetaTable(tableName.getName())) { + checkSystemOrSuperUser(); + } requirePermission("modifyColumn", tableName, columnFamily.getName(), null, Action.ADMIN, - Action.CREATE); + Action.CREATE); } @Override public void preDeleteColumnFamily(ObserverContext ctx, TableName tableName, byte[] columnFamily) throws IOException { + if (isSecurityMetaTable(tableName.getName())) { + if (Bytes.equals(getReservedSecurityMetaColumn(tableName.getName()), columnFamily)) { + throw createSecurityMetaConstraintsViolatedException(tableName, columnFamily.toString()); + } + checkSystemOrSuperUser(); + } requirePermission("deleteColumn", tableName, columnFamily, null, Action.ADMIN, Action.CREATE); } @@ -1157,13 +1202,12 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preDisableTable(ObserverContext c, TableName tableName) throws IOException { - if (Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) { + if (isSecurityMetaTable(tableName.getName())) { // We have to unconditionally disallow disable of the ACL table when we are installed, // even if not enforcing authorizations. We are still allowing grants and revocations, // checking permissions and logging audit messages, etc. If the ACL table is not // available we will fail random actions all over the place. - throw new AccessDeniedException("Not allowed to disable " - + AccessControlLists.ACL_TABLE_NAME + " table with AccessController installed"); + throw createSecurityMetaConstraintsViolatedException(tableName); } requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java index 77f9bc7..ad55bbd 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java @@ -459,12 +459,8 @@ public class TestAccessController extends SecureTestUtil { }; verifyAllowed(disableTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_GROUP_CREATE, - USER_GROUP_ADMIN); + USER_GROUP_ADMIN); verifyDenied(disableTable, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); - - // No user should be allowed to disable _acl_ table - verifyDenied(disableAclTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO, - USER_GROUP_CREATE, USER_GROUP_ADMIN, USER_GROUP_READ, USER_GROUP_WRITE); } @Test @@ -496,7 +492,7 @@ public class TestAccessController extends SecureTestUtil { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preMove(ObserverContext.createAndPrepare(CP_ENV, null), - hri, server, server); + hri, server, server); return null; } }; @@ -1841,7 +1837,7 @@ public class TestAccessController extends SecureTestUtil { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preDeleteSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), - snapshot); + snapshot); return null; } }; @@ -1859,7 +1855,7 @@ public class TestAccessController extends SecureTestUtil { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preCloneSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), - null, null); + null, null); return null; } }; @@ -2613,4 +2609,113 @@ public class TestAccessController extends SecureTestUtil { verifyDenied(replicateLogEntriesAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_ADMIN, USER_GROUP_CREATE); } + + @Test + public void testSecurityMetaConstraints() throws Exception { + AccessTestAction addColumnFamily = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preAddColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME, new HColumnDescriptor(TEST_FAMILY)); + return null; + } + }; + + AccessTestAction modifyReservedColumnFamily = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preModifyColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME, + new HColumnDescriptor(AccessControlLists.ACL_LIST_FAMILY).setMaxVersions(10)); + return null; + } + }; + + AccessTestAction modifyNonReservedColumnFamily = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preModifyColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME, + new HColumnDescriptor(TEST_FAMILY).setMaxVersions(10)); + return null; + } + }; + + + AccessTestAction deleteReservedColumnFamily = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preDeleteColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME, AccessControlLists.ACL_LIST_FAMILY); + return null; + } + }; + + AccessTestAction deleteNonReservedColumnFamily = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preDeleteColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME, TEST_FAMILY); + return null; + } + }; + + + AccessTestAction modifyMetaTable = new AccessTestAction() { + @Override + public Object run() throws Exception { + HTableDescriptor htd = new HTableDescriptor(AccessControlLists.ACL_TABLE_NAME); + htd.addFamily(new HColumnDescriptor(TEST_FAMILY)); + htd.addFamily(new HColumnDescriptor("fam_" + User.getCurrent().getShortName())); + ACCESS_CONTROLLER.preModifyTable(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME, htd); + return null; + } + }; + + + AccessTestAction disableMetaTable = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preDisableTable(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME); + return null; + } + }; + + AccessTestAction deleteMetaTable = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER + .preDeleteTable(ObserverContext.createAndPrepare(CP_ENV, null), + AccessControlLists.ACL_TABLE_NAME); + return null; + } + }; + + verifyAllowed(addColumnFamily, SUPERUSER); + verifyDenied(addColumnFamily, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO); + + verifyAllowed(modifyReservedColumnFamily, SUPERUSER); + verifyDenied(modifyReservedColumnFamily, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO); + + verifyAllowed(modifyNonReservedColumnFamily, SUPERUSER); + verifyDenied(modifyNonReservedColumnFamily, USER_ADMIN, USER_CREATE, USER_OWNER, + USER_RW, USER_RO); + + verifyDenied(deleteReservedColumnFamily, SUPERUSER, USER_ADMIN, USER_CREATE, + USER_OWNER, USER_RW, USER_RO); + + verifyAllowed(deleteNonReservedColumnFamily, SUPERUSER); + verifyDenied(deleteNonReservedColumnFamily, USER_ADMIN, USER_CREATE, USER_OWNER, + USER_RW, USER_RO); + + verifyAllowed(modifyMetaTable, SUPERUSER); + verifyDenied(modifyMetaTable, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO); + + verifyDenied(disableMetaTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO, + USER_GROUP_CREATE, USER_GROUP_ADMIN, USER_GROUP_READ, USER_GROUP_WRITE); + verifyDenied(deleteMetaTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO, + USER_GROUP_CREATE, USER_GROUP_ADMIN, USER_GROUP_READ, USER_GROUP_WRITE); + } }