HBASE-19634 Add permission check for executeProcedures in AccessController

This commit is contained in:
zhangduo 2018-01-04 16:18:21 +08:00
parent 8eeedf0403
commit 1a89793ee8
6 changed files with 103 additions and 42 deletions

View File

@ -126,4 +126,18 @@ public interface RegionServerObserver {
default void postClearCompactionQueues( default void postClearCompactionQueues(
final ObserverContext<RegionServerCoprocessorEnvironment> ctx) final ObserverContext<RegionServerCoprocessorEnvironment> ctx)
throws IOException {} throws IOException {}
/**
* This will be called before executing procedures
* @param ctx the environment to interact with the framework and region server.
*/
default void preExecuteProcedures(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
throws IOException {}
/**
* This will be called after executing procedures
* @param ctx the environment to interact with the framework and region server.
*/
default void postExecuteProcedures(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
throws IOException {}
} }

View File

@ -41,7 +41,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.mutable.MutableObject;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
@ -142,6 +141,7 @@ import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException; import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
import org.apache.hbase.thirdparty.com.google.protobuf.TextFormat; import org.apache.hbase.thirdparty.com.google.protobuf.TextFormat;
import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations;
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter;
import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter; import org.apache.hadoop.hbase.shaded.protobuf.ResponseConverter;
@ -3454,36 +3454,40 @@ public class RSRpcServices implements HBaseRPCErrorHandler,
} }
@Override @Override
@QosPriority(priority = HConstants.ADMIN_QOS)
public ExecuteProceduresResponse executeProcedures(RpcController controller, public ExecuteProceduresResponse executeProcedures(RpcController controller,
ExecuteProceduresRequest request) throws ServiceException { ExecuteProceduresRequest request) throws ServiceException {
if (request.getOpenRegionCount() > 0) { try {
for (OpenRegionRequest req : request.getOpenRegionList()) { checkOpen();
openRegion(controller, req); regionServer.getRegionServerCoprocessorHost().preExecuteProcedures();
} if (request.getOpenRegionCount() > 0) {
} for (OpenRegionRequest req : request.getOpenRegionList()) {
if (request.getCloseRegionCount() > 0) { openRegion(controller, req);
for (CloseRegionRequest req : request.getCloseRegionList()) {
closeRegion(controller, req);
}
}
if (request.getProcCount() > 0) {
for (RemoteProcedureRequest req : request.getProcList()) {
RSProcedureCallable callable;
try {
callable =
Class.forName(req.getProcClass()).asSubclass(RSProcedureCallable.class).newInstance();
} catch (Exception e) {
// here we just ignore the error as this should not happen and we do not provide a general
// way to report errors for all types of remote procedure. The procedure will hang at
// master side but after you solve the problem and restart master it will be executed
// again and pass.
LOG.warn("create procedure of type " + req.getProcClass() + " failed, give up", e);
continue;
} }
callable.init(req.getProcData().toByteArray(), regionServer);
regionServer.executeProcedure(req.getProcId(), callable);
} }
if (request.getCloseRegionCount() > 0) {
for (CloseRegionRequest req : request.getCloseRegionList()) {
closeRegion(controller, req);
}
}
if (request.getProcCount() > 0) {
for (RemoteProcedureRequest req : request.getProcList()) {
RSProcedureCallable callable;
try {
callable =
Class.forName(req.getProcClass()).asSubclass(RSProcedureCallable.class).newInstance();
} catch (Exception e) {
regionServer.remoteProcedureComplete(req.getProcId(), e);
continue;
}
callable.init(req.getProcData().toByteArray(), regionServer);
regionServer.executeProcedure(req.getProcId(), callable);
}
}
regionServer.getRegionServerCoprocessorHost().postExecuteProcedures();
return ExecuteProceduresResponse.getDefaultInstance();
} catch (IOException e) {
throw new ServiceException(e);
} }
return ExecuteProceduresResponse.getDefaultInstance();
} }
} }

View File

@ -205,6 +205,24 @@ public class RegionServerCoprocessorHost extends
}); });
} }
public void preExecuteProcedures() throws IOException {
execOperation(coprocEnvironments.isEmpty() ? null : new RegionServerObserverOperation() {
@Override
public void call(RegionServerObserver observer) throws IOException {
observer.preExecuteProcedures(this);
}
});
}
public void postExecuteProcedures() throws IOException {
execOperation(coprocEnvironments.isEmpty() ? null : new RegionServerObserverOperation() {
@Override
public void call(RegionServerObserver observer) throws IOException {
observer.postExecuteProcedures(this);
}
});
}
/** /**
* Coprocessor environment extension providing access to region server * Coprocessor environment extension providing access to region server
* related services. * related services.

View File

@ -1,4 +1,4 @@
/* /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
@ -117,13 +117,6 @@ import org.apache.hadoop.hbase.security.Superusers;
import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.security.UserProvider; import org.apache.hadoop.hbase.security.UserProvider;
import org.apache.hadoop.hbase.security.access.Permission.Action; import org.apache.hadoop.hbase.security.access.Permission.Action;
import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap;
import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSet;
import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap;
import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
import org.apache.hbase.thirdparty.com.google.common.collect.MapMaker;
import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
import org.apache.hadoop.hbase.util.ByteRange; import org.apache.hadoop.hbase.util.ByteRange;
import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Bytes;
@ -136,6 +129,14 @@ import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap;
import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableSet;
import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap;
import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
import org.apache.hbase.thirdparty.com.google.common.collect.MapMaker;
import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
/** /**
* Provides basic authorization checks for data access and administrative * Provides basic authorization checks for data access and administrative
* operations. * operations.
@ -2507,8 +2508,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor,
@Override @Override
public void preRequestLock(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace, public void preRequestLock(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace,
TableName tableName, RegionInfo[] regionInfos, String description) TableName tableName, RegionInfo[] regionInfos, String description) throws IOException {
throws IOException {
// There are operations in the CREATE and ADMIN domain which may require lock, READ // There are operations in the CREATE and ADMIN domain which may require lock, READ
// or WRITE. So for any lock request, we check for these two perms irrespective of lock type. // or WRITE. So for any lock request, we check for these two perms irrespective of lock type.
String reason = String.format("Description=%s", description); String reason = String.format("Description=%s", description);
@ -2521,12 +2521,18 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor,
checkLockPermissions(ctx, null, tableName, null, description); checkLockPermissions(ctx, null, tableName, null, description);
} }
@Override
public void preExecuteProcedures(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
throws IOException {
checkSystemOrSuperUser(getActiveUser(ctx));
}
/** /**
* Returns the active user to which authorization checks should be applied. * Returns the active user to which authorization checks should be applied.
* If we are in the context of an RPC call, the remote user is used, * If we are in the context of an RPC call, the remote user is used,
* otherwise the currently logged in user is used. * otherwise the currently logged in user is used.
*/ */
public User getActiveUser(ObserverContext<?> ctx) throws IOException { private User getActiveUser(ObserverContext<?> ctx) throws IOException {
// for non-rpc handling, fallback to system user // for non-rpc handling, fallback to system user
Optional<User> optionalUser = ctx.getCaller(); Optional<User> optionalUser = ctx.getCaller();
if (optionalUser.isPresent()) { if (optionalUser.isPresent()) {

View File

@ -202,5 +202,12 @@ public class TestJMXConnectorServer {
throw new AccessDeniedException("Insufficient permissions to shut down cluster."); throw new AccessDeniedException("Insufficient permissions to shut down cluster.");
} }
} }
@Override
public void preExecuteProcedures(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
throws IOException {
// FIXME: ignore the procedure permission check since in our UT framework master is neither
// the systemuser nor the superuser so we can not call executeProcedures...
}
} }
} }

View File

@ -1,4 +1,4 @@
/* /**
* Licensed to the Apache Software Foundation (ASF) under one * Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file * or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information * distributed with this work for additional information
@ -30,7 +30,6 @@ import com.google.protobuf.RpcCallback;
import com.google.protobuf.RpcController; import com.google.protobuf.RpcController;
import com.google.protobuf.Service; import com.google.protobuf.Service;
import com.google.protobuf.ServiceException; import com.google.protobuf.ServiceException;
import java.io.IOException; import java.io.IOException;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.ArrayList; import java.util.ArrayList;
@ -38,7 +37,6 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileSystem;
@ -3064,4 +3062,18 @@ public class TestAccessController extends SecureTestUtil {
verifyAllowed( verifyAllowed(
action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
} }
@Test
public void testExecuteProcedures() throws Exception {
AccessTestAction action = new AccessTestAction() {
@Override
public Object run() throws Exception {
ACCESS_CONTROLLER.preExecuteProcedures(ObserverContextImpl.createAndPrepare(RSCP_ENV));
return null;
}
};
verifyAllowed(action, SUPERUSER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_ADMIN);
}
} }