Integrated Security allows you to control the authentication/authorization of all Geode is introducing additional security features which allow finer grained control for JMX operations as well as GFSH commands. This functionality is automatically activated when the Geode properties security-client-authenticator
and security-client-accessor
are set.
Permissions are designed to be noun-verby and are in the form of RESOURCE:OPERATION[:REGION] tuples. The following values are valid:
Resource
- CLUSTER
- DATA
Operation
- MANAGE
- READ
- WRITE
At the end of this document is a reference list of all JMX and GFSH operations with their corresponding permissions.
To quickly get started using permissions for JMX and GFSH a sample implementation of com.gemstone.gemfire.security.Authenticator
and com.gemstone.gemfire.security.AccessControl
is provided by the class com.gemstone.gemfire.security.templates.SampleJsonAuthorization
. This implementation requires a JSON file which defines the allowed users and their corresponding permissions. For example:
Code Block |
---|
{
"roles": [
{
"name": "cluster",
"operationsAllowed": [
"CLUSTER:MANAGE",
"CLUSTER:WRITE",
"CLUSTER:READ"
]
},
{
"name": "data",
"operationsAllowed": [
"DATA:MANAGE",
"DATA:WRITE",
"DATA:READ"
],
"regions": ["region1", "region2"]
}
]
"users": [
{
"name": "super-user",
"password": "1234567",
"roles": [
"cluster",
"data"
]
},
{
"name": "joebloggs",
"password": "1234567",
"roles": [
"data"
]
}
]
} |
In this example we have two roles defined: cluster and data. The data role only has access to two regions: region1 and region2.
To start using this sample perform the following steps:
Using gfsh, start a locator with security activated.
Code Block language bash gfsh> start locator --name=locator1 \ --J=-Dgemfire.security-client-authenticator=com.gemstone.gemfire.security.templates.SampleJsonAuthorization.create \ --J=-Dgemfire.security-client-accessor=com.gemstone.gemfire.security.templates.SampleJsonAuthorization.create
Similarly, start a server
Code Block gfsh> start server --name=server1 --locators=localhost[10334]
Start a new instance of gfsh and connect with one of the users defined in your JSON file. The super-user should be allowed to do everything in gfsh.
Code Block gfsh> connect --locators=localhost[10334] --user=super-user --password=1234567
Disconnect and reconnect with a user with lesser privileges:
Code Block gfsh> disconnect gfsh> connect --locators=localhost[10334] --user=joebloggs --password=1234567 gfsh> stop server --name=server1 An error occurred while attempting to stop a Cache Server: Subject does not have permission [CLUSTER:READ]
Client-Server Security
You may notice that this new functionality is activated in the same way that the existing client-server authentication and authorization is activated. The intention is that eventually all means of accessing Geode will be secured with exactly the same callbacks.
If you already have an existing implementation of Authenticator and AccessControl no changes to existing code should be necessary. However, you should be aware of the following:
- All Resources are enumerated via the enum
OperationContext.Resource.
- All OperationCodes are enumerated via the enum
OperationContext.OperationCode.
- All of the existing
OperationContext.is*
methods have been deprecated in favor of testing against the relevant enums. - The resource and operation code, for a given context, can be retrieved using
OperationContext.getResource
andOperationContext.getOperationCode
respectively. Existing code, implementing AccessControl, would have needed to check the type of the OperationContext as passed into the
authorizeOperation
method. This is still possible, however it will now be easier to achieve the same functionality by simply checking the Resource and OperationCode of the context. For example, existing code might have looked like this:Code Block language java @Override public boolean authorizeOperation(String regionName, OperationContext context) { if (context instanceof PutOperationContext) { // cast to PutOperationContext } else if (context instanceof QueryOperationContext) { // cast to QueryOperationContext } else { // Must be JMX or CLI } return false; }
Can now be changed to:
Code Block language java @Override public boolean authorizeOperation(String regionName, OperationContext context) { switch (context.getOperationCode()) { case PUT: // cast to PutOperationContext break; case QUERY: // cast to QueryOperationContext break; case READ: case WRITE: case MANAGE: // Must be JMX or CLI - no need to cast; just use the 'context' as is break; } return false; }
Note that any JMX or CLI contexts are not associated with a specific sub-type of OperationContext and can be handled without having to cast the context parameter.
- All client-server operations are associated with a Resource of DATA.
Reference
Client-Server
Client-server permissions are associated with their respective OperationContexts as follows. Permissions appear as Resource:OperationCode
tuples.
OperationContext | Permission |
---|---|
CloseCQOperationContext | DATA:CLOSE_CQ |
ContainsKeyOperationContext | DATA:CONTAINS_KEY |
DestroyOperationContext | DATA:DESTROY |
ExecuteCQOperationContext | DATA:EXECUTE_CQ |
ExecuteFunctionOperationContext | DATA:EXECUTE_FUNCTION |
GetDurableCQsOperationContext | DATA:GET_DURABLE_CQS |
GetOperationContext | DATA:GET |
InvalidateOperationContext | DATA:INVALIDATE |
KeySetOperationContext | DATA:KEY_SET |
PutAllOperationContext | DATA:PUTALL |
PutOperationContext | DATA:PUT |
QueryOperationContext | DATA:QUERY |
RegionClearOperationContext | DATA:REGION_CLEAR |
RegionCreateOperationContext | DATA:REGION_CREATE |
RegionDestroyOperationContext | DATA:REGION_DESTROY |
RegisterInterestOperationContext | DATA:REGISTER_INTEREST |
RemoveAllOperationContext | DATA:REMOVEALL |
StopCQOperationContext | DATA:STOP_CQ |
UnregisterInterestOperationContext | DATA:UNREGISTER_INTEREST |
GFSH and JMX
Following are lists for gfsh commands, (highlighted in green), and JMX operations with their corresponding permissions. Permissions appear as Resource:OperationCode
tuples.
entities within one implementation. When Integrated Security is turned on, all client/server communications, peer to peer, gateway authentication, jmx operations, gfsh commands and Pulse are protected with this single security mechanism.
1. No changes for legacy implementations of Authentication/Authorization for client-server security
The API for Authenticator and AccessControl has not changed. Your implementations of those are still honored in client/server communication if you choose not to implement the new security interface. However, these two are deprecated now and will be removed in the future.
2. Introduction of security-manager configuration and SecurityManager interface for Integrated Security
To turn on integrated security, start your servers/locators with the security-manager
property set in your gemfire.properties
file:
Code Block | ||
---|---|---|
| ||
security-manager = com.example.security.MySecurityManager |
The security-manager
property identifies the class name of the SecurityManager
interface implementation. SecurityManager
is the interface you implement for both authentication and authorization. Make sure your class has a zero argument constructor so that Geode can instantiate the object. See the SecurityManager
javadoc for details. There is an ExampleSecurityManager
in the geode-core/src/main/java/org/apache/geode/examples/security
directory that you can use as an example to write your own implementation.
3. Introduction of ResourcePermission
In SecurityManager
, you will see that a ResourcePermission
is passed in the authorization call. ResourcePermission
is an object that defines the nature of the operation the Principal is trying to perform.
ResourcePermission
is in one of those forms:
Code Block | ||
---|---|---|
| ||
Resource:Operation
Resource:Operation:RegionName
Resource:Operation:RegionName:Key |
All Resources are enumerated via the enum ResourcePermission.Resource
, currently CLUSTER
and DATA.
All Operations are enumerated via the enum ResourcePermission.Operation
, which are MANAGE
, READ
, and WRITE
. Note that MANAGE
does not imply WRITE
, and WRITE
does not imply READ
.
RegionName
and Key
are provided for those operations that are to be authorized based upon a region key access as well. For example, you can see a ResourcePermission
defined as CLUSTER:READ
, CLUSTER:MANAGE
, DATA:READ
, DATA:READ:regionA
, or DATA:READ:regionA:key1
.
Note that a ResourcePermission
is hierarchical. If a principal has permission for DATA:READ
, it automatically has data read permission on all regions and all keys. That is, it has permission for DATA:READ:regionA
. Given permission for DATA:READ:regionA
, the principal automatically has data read permission on all keys in regionA. That is, it has permission for DATA:READ:regionA:key1
.
4. Introduction of PostProcessor
Before a value is returned, it gets a pass through the post processor, if there is one. Specify your post processor with this line in your gemfire.properties
file:
Code Block | ||
---|---|---|
| ||
security-post-processor = com.example.security.MyPostProcessor |
where the value is the name of the class that implements the PostProcessor
interface. Make sure your class has a zero argument constructor so that Geode can instantiate the object. See the PostProcessor
javadoc for details. You can use SamplePostProcessor
as an example to write your own implementation.
Note regarding legacy implementations: We completely redo the way we call post processing; the interface is a lot simpler.
5. Operations and their corresponding ResourcePermission
Below is the list of operations with their corresponding ResourcePermission
:
Client-Server Operations
Client Operations | Required ResourcePermission |
---|---|
get function attribute | CLUSTER:READ |
create region | DATA:MANAGE |
destroy region | DATA:MANAGE |
get keyset | DATA:READ:regionName |
query | DATA:READ:regionName |
region.getAll | DATA:READ:regionName |
region.getEntry | DATA:READ:regionName |
getAll (list of keys) | DATA:READ:regionName:key |
region.containsKeyOnServer(key) | DATA:READ:regionName:key |
region.get(key) | DATA:READ:regionName:key |
registerInterest | DATA:READ:regionName:key if key is specified, otherwise DATA:READ:regionName |
unregister interest | DATA:READ:regionName:key if key is specified, otherwise DATA:READ:regionName |
execute function | DATA:WRITE |
clear region | DATA:WRITE:regionName |
putAll | DATA:WRITE:regionName |
region.clear | DATA:WRITE:regionName |
region.removeAll | DATA:WRITE:regionName |
destroy key | DATA:WRITE:regionName:key |
invalidate key | DATA:WRITE:regionName:key |
region.destroy(key) | DATA:WRITE:regionName:key |
region.invalidate(key) | DATA:WRITE:regionName:key |
region.put(key, value) | DATA:WRITE:regionName:key |
region.replace | DATA:WRITE:regionName:key |
GFSH Commands
Commands | Required ResourcePermission | |||
---|---|---|---|---|
Cluster MANAGEment Operations | Permission | |||
alter runtime | CLUSTER:MANAGE | |||
gc | CLUSTER:MANAGE | |||
shutdown | CLUSTER:MANAGE | |||
startManager | CLUSTER:MANAGE | |||
stop locator --name=locator1 | CLUSTER:MANAGE | |||
stop server --name=server1 | CLUSTER:MANAGE | |||
DistributedSystemMXBean.shutdownAllMembers | CLUSTER:MANAGE | |||
ManagerMXBean.start | CLUSTER:MANAGE | |||
ManagerMXBean.stop | CLUSTER:MANAGE | |||
MemberMXBean.createManager()) | CLUSTER:MANAGE | |||
MemberMXBean.shutDownMember | CLUSTER:MANAGE | |||
Cluster READ Operations | Permission | |||
countDurableCqEvents | CLUSTER:READ | |||
describe client --clientID=172.16.196.144 | CLUSTER:READ | |||
describe config --member=Member1 | CLUSTER:READ | |||
describe disk-store --name=foo --member=baz | CLUSTER:READ | |||
describe member --name=server1 | CLUSTER:READ | |||
describe offline-disk-store --name=foo --disk-dirs=bar | CLUSTER:READ | |||
describe region --name=value | CLUSTER:READ | |||
export cluster-configuration --zip-file-name=mySharedConfig.zip | CLUSTER:READ | |||
export config --member=member1 | CLUSTER:READ | |||
export logs --dir=data/logs | CLUSTER:READ | |||
export stack-traces --file=stack.txt | CLUSTER:READ | |||
exportLogs | CLUSTER:READ | |||
exportStackTrace | CLUSTER:READ | |||
list async-event-queues | CLUSTER:READ | |||
list clients | CLUSTER:READ | |||
list deployed | CLUSTER:READ | |||
list disk-stores | CLUSTER:READ | |||
list durable-cqs --durable-client-id=client1 | CLUSTER:READ | |||
list functions | CLUSTER:READ | |||
list gateways | CLUSTER:READ | |||
list indexes | CLUSTER:READ | |||
list members | CLUSTER:READ | |||
list regions | CLUSTERDATA:READ | |||
netstat --member=server1 | CLUSTER:READ | |||
show dead-locks --file=deadlocks.txt | CLUSTER:READ | |||
show log --member=locator1 --lines=5 | CLUSTER:READ | |||
show metrics | CLUSTER:READ | |||
show missing-disk-stores | CLUSTER:READ | |||
show subscription-queue-size --durable-client-id=client1 | CLUSTER:READ | |||
show log | CLUSTER:READ | |||
status cluster-config-service | CLUSTER:READ | |||
status gateway-receiver | CLUSTER:READ | |||
status gateway-sender | CLUSTER:READ | |||
Mbeans get attributes | CLUSTER:READ | |||
MemberMXBean.showLog | CLUSTER:READ | Cluster WRITE OperationsPermission | ||
change loglevel --loglevel=severe --member=server1 | CLUSTER:WRITE | |||
DistributedSystemMXBean.changeAlertLevel | CLUSTER:WRITE | |||
ManagerMXBean.setPulseURL | CLUSTER:WRITE | |||
CLUSTER:WRITE | Data MANAGE Operations | Permission|||
alter disk-store --name=foo --region=xyz --disk-dirs=bar | DATA:MANAGE | |||
alter region --name=region1 --eviction-max=5000 | DATA:MANAGE:REGIONNAME | |||
clear defined indexes | DATA:MANAGE | |||
close durable-client --durable-client-id=client1 | DATA:MANAGE | |||
close durable-cq --durable-client-id=client1 --durable-cq-name=cq1 | DATA:MANAGE | |||
compact disk-store --name=foo | DATA:MANAGE | |||
compact offline-disk-store --name=foo --disk-dirs=bar | DATA:MANAGE | |||
configure pdx --read-serialized=true | DATA:MANAGE | |||
create async-event-queue --id=myAEQ --listener=myApp.myListener | DATA:MANAGE | |||
create defined indexes | DATA:MANAGE | |||
create disk-store --name=foo --dir=bar | DATA:MANAGE | |||
create gateway-receiver | DATA:MANAGE | |||
create gateway-sender --id=sender1 --remote-distributed-system-id=2 | DATA:MANAGE | |||
create index --name=myKeyIndex --expression=region1.Id --region=region1 --type=key | DATA:MANAGE: | REGIONNAMEregionName | ||
create region --name=region12 | DATA:MANAGE | |||
define index --name=myIndex1 --expression=exp1 --region=/exampleRegion | DATA:MANAGE: | REGIONNAMEregionName | ||
deploy --jar=group1_functions.jar --group=Group1 | DATA:MANAGE | |||
destroy disk-store --name=foo | DATA:MANAGE | |||
destroy function --id=InterestCalculations | DATA:MANAGE | |||
destroy index --member=server2 | DATA:MANAGE | :REGIONNAME:regionName if regionName is specified, otherwise DATA:MANAGE | ||
destroy region --name=value | DATA:MANAGE | |||
import cluster-configuration --zip-file-name=value | DATA:MANAGE | |||
load-balance gateway-sender --id=sender1 | DATA:MANAGE | |||
pause gateway-sender --id=sender1 | DATA:MANAGE | |||
pdx rename --old=com.gemstone --new=com.pivotal --disk-store=ds1 --disk-dirs=/diskDir1 | DATA:MANAGE | |||
rebalance --include | -region=region1DATA:MANAGE | remove- | -region=region1 | DATA:MANAGE |
resume gateway-sender --id=sender1 | DATA:MANAGE | |||
revoke missing-disk-store --id=foo | DATA:MANAGE | |||
start gateway-receiver | DATA:MANAGE | |||
start gateway-sender --id=sender1 | DATA:MANAGE | |||
stop gateway-receiver | DATA:MANAGE | |||
stop gateway-sender --id=sender1 | DATA:MANAGE | |||
undeploy --group=Group1 | DATA:MANAGE | |||
backup disk-store --dir=foo | DATA:READ | |||
export data --region=region1 --file=foo.txt --member=value | DATA:READ:regionName | |||
get --key=key1 --region=region1 | DATA:READ:regionName:key | |||
locateEntry | DATA:READ:regionName:key | |||
query --query='SELECT * FROM /region1' | DATA:READ:REGIONNAME | |||
execute function --id=InterestCalculations --group=Group1 | DATA:WRITE | |||
import data --region=region1 --file=foo.txt --member=value | DATA:WRITE:regionName | |||
put --key=key1 --value=value1 --region=region1 | DATA:WRITE:regionName:key | |||
remove --region=region1 | DATA:WRITE:regionName, if key is specified, then DATA:WRITE:regionName:key | |||
alter jdbc-connection | CLUSTER:MANAGE | |||
alter jdbc-mapping | CLUSTER:MANAGE | |||
create jdbc-connection | CLUSTER:MANAGE | |||
create jdbc-mapping | CLUSTER:MANAGE | |||
describe jdbc-connection | CLUSTER:MANAGE | |||
describe jdbc-mapping | CLUSTER:MANAGE | |||
destroy jdbc-connection | CLUSTER:MANAGE | |||
destroy jdbc-mapping | CLUSTER:MANAGE | |||
list jdbc-connections | CLUSTER:MANAGE | |||
list jdbc-mappings | CLUSTER:MANAGE | |||
create jndi-binding | CLUSTER:MANAGE | |||
describe jndi-binding | CLUSTER:READ | |||
destroy jndi-binding | CLUSTER:MANAGE | |||
list jndi-binding | CLUSTER:READ |
Pulse
Pulse access is divided into two main categories: access to the Data Browser page and everything else.
The Data Browser page requires the permissions CLUSTER:READ and DATA:READ. Access to all other pages requires only CLUSTER:READ permission.
JMX Operations
Bean Operations | Permission |
---|---|
DistributedSystemMXBean.shutdownAllMembers | CLUSTER:MANAGE |
ManagerMXBean.start | CLUSTER:MANAGE |
ManagerMXBean.stop | CLUSTER:MANAGE |
MemberMXBean.createManager()) | CLUSTER:MANAGE |
MemberMXBean.shutDownMember | CLUSTER:MANAGE |
Mbeans get attributes | CLUSTER:READ |
MemberMXBean.showLog | CLUSTER:READ |
DistributedSystemMXBean.changeAlertLevel | CLUSTER:WRITE |
ManagerMXBean.setPulseURL | CLUSTER:WRITE |
ManagerMXBean.setStatusMessage | CLUSTER:WRITE |
CacheServerMXBean.closeAllContinuousQuery | DATA:MANAGE |
CacheServerMXBean.closeContinuousQuery | DATA:MANAGE |
CacheServerMXBean.removeIndex("foo")) | DATA:MANAGE |
CacheServerMXBean.stopContinuousQuery("bar")) | DATA:MANAGE |
DiskStoreMXBean.flush()) | DATA:MANAGE |
DiskStoreMXBean.forceCompaction()) | DATA:MANAGE |
DiskStoreMXBean.forceRoll()) | DATA:MANAGE |
DiskStoreMXBean.setDiskUsageCriticalPercentage(0 | DATA:MANAGE |
DiskStoreMXBean.setDiskUsageWarningPercentage(0 | DATA:MANAGE |
DistributedSystemMXBean.revokeMissingDiskStores | DATA:MANAGE |
DistributedSystemMXBean.setQueryCollectionsDepth | DATA:MANAGE |
DistributedSystemMXBean.setQueryResultSetLimit | DATA:MANAGE |
GatewayReceiverMXBean.pause()) | DATA:MANAGE |
GatewayReceiverMXBean.rebalance()) | DATA:MANAGE |
GatewayReceiverMXBean.resume()) | DATA:MANAGE |
GatewayReceiverMXBean.start | DATA:MANAGE |
GatewayReceiverMXBean.stop | DATA:MANAGE |
GatewaySenderMXBean.pause | DATA:MANAGE |
GatewaySenderMXBean.rebalance | DATA:MANAGE |
GatewaySenderMXBean.resume | DATA:MANAGE |
GatewaySenderMXBean.start | DATA:MANAGE |
GatewaySenderMXBean.stop | DATA:MANAGE |
LockServiceMBean.becomeLockGrantor()) | DATA:MANAGE |
MemberMXBean.compactAllDiskStores | DATA:MANAGE |
Data READ Operations | Permission |
backup disk-store --dir=foo | DATA:READ|
export data --region=region1 --file=foo.txt --member=value | DATA:READ:REGIONNAME |
get --key=key1 --region=region1 | DATA:READ:REGIONNAME |
locateEntry | DATA:READ:REGIONNAME |
query --query='SELECT * FROM /region1' | DATA:READ:REGIONNAME |
CacheServerMXBean.executeContinuousQuery("bar")) | DATA:READ |
DistributedSystemMXBean.backupAllMembers | DATA:READ |
DistributedSystemMXBean.queryData | DATA:READ |
DistributedSystemMXBean.queryDataForCompressedResult | DATA:READ |
Data WRITE Operations | Permission |
execute function --id=InterestCalculations --group=Group1 | DATA:WRITE |
import data --region=region1 --file=foo.txt --member=value | DATA:WRITE:REGIONNAME |
put --key=key1 --value=value1 --region=region1 | DATA:WRITE:REGIONNAME
Content by Label | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
...