Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

By invoking these QueryChecker APIs, CloudStack API engine can pre-construct proper SQL where clause to achieve proper row filter for accessibility control.

Response View

Currently CloudStack provides different response views for Root admin and non-root user, some response fields are only visible to root admin. Basically we have provided two static response views (Full view and Restricted view), domain admin will also a User view. With new IAM service introduced, we should also allow customers to be able to specify what view should be applied to the new Acl group when they are creating a new customized Acl group, for example, customer care group. To achieve that, we will implement as follows:

  1. We will have a column in AclGroup db table to record what view to be used for this group. From Acl group creation UI, user can pick which view to be associated with this group. Note that in this release, we are not going to support full-fledged column filter (that is, allowing users to pick arbitrary columns to be see for each API). We are only supporting static view association at the Acl group level.
  2. We will separate all current both admin and user allowed API commands to two classes: API for admin and API for user. For example, previous ListVMsCmd will be splitted into two classes: ListVMsCmdByAdmin and ListVMsCmd.

    Code Block
    @APICommand(name = "listVirtualMachines", description = "List the virtual machines owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted)
    public class ListVMsCmd extends BaseListTaggedResourcesCmd {
    ......
    }
    
    @APICommand(name = "listVirtualMachines", description = "List the virtual machines owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Full)
    public class ListVMsCmdByAdmin extends ListVMsCmd {
        /////////////////////////////////////////////////////
        //////////////// API parameters /////////////////////
        /////////////////////////////////////////////////////
    
        @Parameter(name=ApiConstants.HOST_ID, type=CommandType.UUID, entityType=HostResponse.class,
                description="the host ID")
        private Long hostId;
    
        @Parameter(name=ApiConstants.POD_ID, type=CommandType.UUID, entityType=PodResponse.class,
                description="the pod ID")
        private Long podId;
    
        @Parameter(name=ApiConstants.STORAGE_ID, type=CommandType.UUID, entityType=StoragePoolResponse.class,
                description="the storage ID where vm's volumes belong to")
        private Long storageId;
    }
    

    Note that ListVMsCmdByAdmin and ListVMsCmd are sharing the same API name "listVirtualMachines". From client perspective, this is transparent to them, CloudStack API client will still just invoke previous listVirtualMachine API, and CloudStack API server will internally consult with IAM plugin to determine the group associated with the invoking user and then determine which internal Cmd class to be invoked. There is a new attribute "responseView" introduced for @APICommand annoation, which can be used to instruct CloudStack to generate different response view. By separating the command class into admin cmd class and user cmd class, we can also restrict valid input parameters for different account. For example, in this case, when Admin invokes listVirtualMachine api, he/she can pass hostId, podId and storageId, which parameters are not applicable for end user.

Default Policy

Default Policy Generation Flow

For backward compatibility, out-of-box, we provided 3 default policies: Root admin policy, domain admin policy, and end user policy. The process to populate permissions for these default policies are as follows:

  1. Parse all API command classes to check if they are annotated with "authorized" attribute. If so, populate a permission entry for that API for the default policy mapped to that authorized role.
  2. Parse commands.property, create or override permission entries for each API for default policy mapped to API authorized roles.

When user creates a customized policy, he can specify a source policy from list of default policies. Then we will create new mapping between these default policy permission entries and the new policy in acl_policy_permission_map table.

Sample DB Entries for Default Policy

account

id

account_name

uuid

domain_id

2

admin

1c5afd64-482b-11e3-86f3-8118f47f9f9f

1

3

domainAdmin

929d172c-b95e-4b86-9474-9789072c9bdb

2

4

domainUserA

f96ddb47-d3c0-4360-a9cd-613d631c8333

2

acl_group

id

name

description

uuid

path

removed

created

1

REGULAR_USER

Domain user group

d283d4f0-31f0-11e3-ad37-80f85ce25918

/

NULL

2013-10-10 14:13:34

2

ADMIN

Root admin group

d283de28-31f0-11e3-ad37-80f85ce25918

/

NULL

2013-10-10 14:13:34

3

DOMAIN_ADMIN

Domain admin group

d283e6e8-31f0-11e3-ad37-80f85ce25918

/

NULL

2013-10-10 14:13:34

acl_group_account_map

Component Design

IAM feature will be making use of CloudStack's plugin framework and will be implemented as a separate plugin. The end goal is to be able to host the IAM server as an independent service listening at an endpoint which the CS API service calls to do access checks.

IAM plugin will be developed under cloudstack-services project so that in future it can be easily separated as a service.

Following is the code base structure for the IAM feature:

 

Image Added

 

cloud-iam service will be having two parts:

  •  plugin
    • This will be a CloudStack plugin and will integrate with CloudStack API/Server projects for all ACL related functionality through adapter implementations that get injected in the core CS components.
    • Contains implementations of all the ACL related adapter interfaces of CloudStack namely, SecurityChecker, APIChecker and the new QuerySelector
    • Contains the new ACL APIs related to group/policy/permission CRUD operations and APIS related to account-group associations.
  •  server
    • This will be an implementation of the pure IAM/RBAC model and is independent of any CloudStack terminology
    • Contains IAM interfaces dealing with group-policy-permission
    • Contains a GenericDao based implementation of these interfaces
    • Thus in order to work with a different IAM implementation say LDAP/AD based, one will have to implement the IAM server interfaces. The plugin portion and its integration with CS will not be affected.

...

 

Following component diagram illustrates these boundaries:

Gliffy Diagram
nameIAM_component_deployment

 

Response View

Currently CloudStack provides different response views for Root admin and non-root user, some response fields are only visible to root admin. Basically we have provided two static response views (Full view and Restricted view), domain admin will also a User view. With new IAM service introduced, we should also allow customers to be able to specify what view should be applied to the new Acl group when they are creating a new customized Acl group, for example, customer care group. To achieve that, we will implement as follows:

  1. We will have a column in AclGroup db table to record what view to be used for this group. From Acl group creation UI, user can pick which view to be associated with this group. Note that in this release, we are not going to support full-fledged column filter (that is, allowing users to pick arbitrary columns to be see for each API). We are only supporting static view association at the Acl group level.
  2. We will separate all current both admin and user allowed API commands to two classes: API for admin and API for user. For example, previous ListVMsCmd will be splitted into two classes: ListVMsCmdByAdmin and ListVMsCmd.

    Code Block
    @APICommand(name = "listVirtualMachines", description = "List the virtual machines owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted)
    public class ListVMsCmd extends BaseListTaggedResourcesCmd {
    ......
    }
    
    @APICommand(name = "listVirtualMachines", description = "List the virtual machines owned by the account.", responseObject = UserVmResponse.class, responseView = ResponseView.Full)
    public class ListVMsCmdByAdmin extends ListVMsCmd {
        /////////////////////////////////////////////////////
        //////////////// API parameters /////////////////////
        /////////////////////////////////////////////////////
    
        @Parameter(name=ApiConstants.HOST_ID, type=CommandType.UUID, entityType=HostResponse.class,
                description="the host ID")
        private Long hostId;
    
        @Parameter(name=ApiConstants.POD_ID, type=CommandType.UUID, entityType=PodResponse.class,
                description="the pod ID")
        private Long podId;
    
        @Parameter(name=ApiConstants.STORAGE_ID, type=CommandType.UUID, entityType=StoragePoolResponse.class,
                description="the storage ID where vm's volumes belong to")
        private Long storageId;
    }
    

    Note that ListVMsCmdByAdmin and ListVMsCmd are sharing the same API name "listVirtualMachines". From client perspective, this is transparent to them, CloudStack API client will still just invoke previous listVirtualMachine API, and CloudStack API server will internally consult with IAM plugin to determine the group associated with the invoking user and then determine which internal Cmd class to be invoked. There is a new attribute "responseView" introduced for @APICommand annoation, which can be used to instruct CloudStack to generate different response view. By separating the command class into admin cmd class and user cmd class, we can also restrict valid input parameters for different account. For example, in this case, when Admin invokes listVirtualMachine api, he/she can pass hostId, podId and storageId, which parameters are not applicable for end user.

Default Policy

Default Policy Generation Flow

For backward compatibility, out-of-box, we provided 3 default policies: Root admin policy, domain admin policy, and end user policy. The process to populate permissions for these default policies are as follows:

  1. Parse all API command classes to check if they are annotated with "authorized" attribute. If so, populate a permission entry for that API for the default policy mapped to that authorized role.
  2. Parse commands.property, create or override permission entries for each API for default policy mapped to API authorized roles.

When user creates a customized policy, he can specify a source policy from list of default policies. Then we will create new mapping between these default policy permission entries and the new policy in acl_policy_permission_map table.

Sample DB Entries for Default Policy

account

id

account_name

uuid

domain_id

2

admin

1c5afd64-482b-11e3-86f3-8118f47f9f9f

1

3

domainAdmin

929d172c-b95e-4b86-9474-9789072c9bdb

2

4

domainUserA

f96ddb47-d3c0-4360-a9cd-613d631c8333

2

acl_group

id

name

description

uuid

path

removed

created

1

REGULAR_USER

Domain user group

d283d4f0-31f0-11e3-ad37-80f85ce25918

/

id

group_id

account_id

removed

created

2

2

2

NULL

2013-10-10 14:13:34

323

ADMIN

3

NULL

2013-10-11 00:14:54

4

1

Root admin group

d283de28-31f0-11e3-ad37-80f85ce25918

/4

NULL

2013-10-11 0010 14:1913:5534

acl_policy

3

DOMAIN_ADMIN

Domain admin group

d283e6e8

id

name

description

uuid

path

removed

created

policy_type

1

REGULAR_USER

Domain user role

d2838dce-31f0-11e3-ad37-80f85ce25918

/

NULL

2013-10-10 14:13:34

Static

2

ADMIN

Root admin role

d2839c56-31f0-11e3-ad37-80f85ce25918

acl_group_account_map

id

group_id

account_id

removed

created

2

2

2/

NULL

2013-10-10 14:13:34

Static3

3

3

NULL

2013-10-11 00:14:54

4

1

4

DOMAIN_ADMIN

Domain admin role

d283a7f0-31f0-11e3-ad37-80f85ce25918

/

NULL

2013-10-10 1411 00:13:34

Static

19:55

acl_policy

id

name

description

uuid

path

removed

created

policy_type

1

REGULAR_USER

Domain user role

d2838dce

6

RESOURCE_OWNER

Resource owner role

d283c794-31f0-11e3-ad37-80f85ce25918

/

NULL

2013-10-10 14:13:34

Dynamic

acl_group_policy_map

Static

2

ADMIN

Root admin role

d2839c56-31f0-11e3-ad37-80f85ce25918

/

id

group_id

policy_id

removed

created

1

1

1

NULL

2013-10-10 14:13:34

2Static2

3

2

NULL

2013-10-10 14:13:34

DOMAIN_ADMIN

Domain admin role

d283a7f0-31f0-11e3-ad37-80f85ce25918

/

3

3

3

NULL

2013-10-10 14:13:34

Sample DB entries for the policy permissions for 'StartVM' operation:

acl_permission

Static

6

RESOURCE_OWNER

Resource owner role

d283c794-31f0-11e3-ad37-80f85ce25918

/

NULL

2013-10-10 14:13:34

Dynamic

acl_group_policy_map

id

group_id

policy_id

id

action

resource_type

scope_id

scope

access_type

permission

removed

created

1

startVirtualMachine1

VirtualMachine1

NULL

ALL

NULL

Allow

NULL

2013-2013-10-10 14:13:34

2

startVirtualMachine2

VirtualMachine2

$domainId

Domain

NULLAllow

NULL

2013-10-10 14:13:34

3

startVirtualMachine

VirtualMachine

$accountId

3

3Account

NULLAllow

NULL

2013-10-10 14:13:34

Sample DB entries for the policy permissions for 'StartVM' operation:

acl_policy_permission_mappermission

id

action

resource_type

scope_id

scope

access_type

permission

id

policy_id

permission_id

removed

created

1

6startVirtualMachine

3VirtualMachine

NULL

ALL

NULL

Allow

NULL

2013-2013-10-10 14:13:34

2

2

startVirtualMachine

VirtualMachine

$domainId

Domain

NULL

Allow1

NULL

2013-10-10 14:13:34

3

3

startVirtualMachine

VirtualMachine

$accountId

Account

NULL

Allow2

NULL

2013-10-10 14:13:34

Access Check Flow

Lets consider the StartVM API is being called by a user and run through the access control usecase for each default policy.

The StartVMCmd will contain an annotation on the field that needs to be checked for access:

acl_policy_permission_map

id

policy_id

permission_id

removed

created

1

6

3

NULL

2013-10-10 14:13:34

2

2

1

NULL

2013-10-10 14:13:34

3

3

2

NULL

2013-10-10 14:13:34

Access Check Flow

Lets consider the StartVM API is being called by a user and run through the access control usecase for each default policy.

The StartVMCmd will contain an annotation on the field that needs to be checked for access:

Code Block
@APICommand(name = "startVirtualMachine", responseObject = UserVmResponse.class, description = "Starts 
Code Block
@APICommand(name = "startVirtualMachine", responseObject = UserVmResponse.class, description = "Starts a virtual machine.")
public class StartVMCmd extends BaseAsyncCmd {
    public static final Logger s_logger = Logger.getLogger(StartVMCmd.class.getName());

    private static final String s_name = "startvirtualmachineresponse";

    // ///////////////////////////////////////////////////
    // ////////////// API parameters /////////////////////
    // ///////////////////////////////////////////////////

    @ACL(action="startVirtualMachine")
    @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType=UserVmResponse.class,
            required = true, description = "The ID of the virtual machine")
    private Long id;

Regular user 'domainUserA' calls this command for his own VM:

API access check

The API layer will call the APICheckers to see if the user is allowed to invoke this API. The PolicyBasedAccessChecker :: checkAccess(user, apiName) will just return true and rely on next step

Entity Access Check

The @ACL annotation invokes the SecurityChecker implementation. The PolicyBasedAccessChecker:: checkAccess(Account caller, ControlledEntity entity, AccessType accessType, String action) is invoked for the given user account and VM Id for action 'startVirtualMachine'

  • Find all groups the user belongs to: groupIDs = 1
  • Find all 'Effective' policies the groups are associated to: policies = 1, 6
  • If any policy 'Allows' the startVirtualMachine API for this Vm Id, grant permission to make this call: Policy Id 6 and Permission Id 3 allow the API to be invoked for this user.
  • In this case, since this is a regular user and the user is the owner of the VM, then he is granted access using policy Id 6.

A Domain Admin 'domainAdmin' calls this command for a VM in his domain:

Entity Access Check

  • Find all groups the user belongs to: groupIDs = 3
  • Find all 'Effective' policies the groups are associated to: policies = 3
  • Policy Id 3 and Permission Id 2 allow 'startVirtualMachine' access for VMs in the 'Domain' scope - VMs in the domainId of the user.
  • In this case, since the VM is in the domain of the user, he is granted access using policy Id 3.

A Root Admin 'admin' calls this command for any VM:



    @ACL(action="startVirtualMachine")
    @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType=UserVmResponse.class,
            required = true, description = "The ID of the virtual machine")
    private Long id;

Regular user 'domainUserA' calls this command for his own VM:

API access check

The API layer will call the APICheckers to see if the user is allowed to invoke this API. The PolicyBasedAccessChecker :: checkAccess(user, apiName) will just return true and rely on next step

Entity Access Check

The @ACL annotation invokes the SecurityChecker implementation. The PolicyBasedAccessChecker:: checkAccess(Account caller, ControlledEntity entity, AccessType accessType, String action) is invoked for the given user account and VM Id for action 'startVirtualMachine'Entity Access Check

  • Find all groups the user belongs to: groupIDs = 2
  • Find all 'Effective' policies the groups are associated to: policies = 2
  • Policy Id 3 and Permission Id 1 allow 'startVirtualMachine' access for ALL VMs .

@ACL annotation changes

  • As illustrated in the above access flow, the access checks get invoked when the resource Ids in the API Cmd are annotated.
  • Thus we will have to edit all existing API Cmds and add the relevant @ACL annotation on the primary resource Ids the command operates on.
  • For any other resources that the command works with, the current access checks placed in the service layer will invoke the SecurityChecker.
  • These current access checks pass the AccessType whereever needed or mostly pass null. Our SecurityChecker will interpret null as a 'Read' access. In the 'acl_permission' schema, all List* APIs will be marked as 'Read' AccessType entries to facilitate this access check.

Creating Custom Group and Policy

Consider following example:

A Domain admin wants to create a 'Service Desk' group for his domain and allow 'ready only' access to the group for all VMs and Volumes within the Domain.

Steps:

  • createAclGroup('Service Desk', 'Service Desk group', $domainId of the admin)
  • createAclPolicy('Read Only Access', 'read only access to domain resources', $domainId of the admin)
  • createAclPermission('ListVirtualMachine', 'Allow', 'Domain', $domainId, 'VirtualMachine')
  • createAclPermission('ListVolumes', 'Allow', 'Domain', $domainId, 'Volume')
  • addAclPermissionToAclPolicy( UUID of the 'Read Only Access' policy, List<String> permissionIds of above permissions)
  • attachAclPolicyToAclGroup (groupId, policyId)
  • addAccountToAclGroup(groupId, List<String> accountIds)

What should be the method of ‘creating a custom Policy/Role’?
For this release, creating a custom policy is supported through API only.
For further releases, we can provide either a UI or a config file + policy language mechanism to facilitate the custom policy creation.

Component Design

IAM feature will be making use of CloudStack's plugin framework and will be implemented as a separate plugin. The end goal is to be able to host the IAM server as an independent service listening at an endpoint which the CS API service calls to do access checks.

IAM plugin will be developed under cloudstack-services project so that in future it can be easily separated as a service.

Following is the code base structure for the IAM feature:

 

Image Removed

 

cloud-iam service will be having two parts:

  •  plugin
    • This will be a CloudStack plugin and will integrate with CloudStack API/Server projects for all ACL related functionality through adapter implementations that get injected in the core CS components.
    • Contains implementations of all the ACL related adapter interfaces of CloudStack namely, SecurityChecker, APIChecker and the new QuerySelector
    • Contains the new ACL APIs related to group/policy/permission CRUD operations and APIS related to account-group associations.
  •  server
    • This will be an implementation of the pure IAM/RBAC model and is independent of any CloudStack terminology
    • Contains IAM interfaces dealing with group-policy-permission
    • Contains a GenericDao based implementation of these interfaces
    • Thus in order to work with a different IAM implementation say LDAP/AD based, one will have to implement the IAM server interfaces. The plugin portion and its integration with CS will not be affected.

...

 

Following component diagram illustrates these boundaries:

  • Find all 'Effective' policies the groups are associated to: policies = 1, 6
  • If any policy 'Allows' the startVirtualMachine API for this Vm Id, grant permission to make this call: Policy Id 6 and Permission Id 3 allow the API to be invoked for this user.
  • In this case, since this is a regular user and the user is the owner of the VM, then he is granted access using policy Id 6.

A Domain Admin 'domainAdmin' calls this command for a VM in his domain:

Entity Access Check

  • Find all groups the user belongs to: groupIDs = 3
  • Find all 'Effective' policies the groups are associated to: policies = 3
  • Policy Id 3 and Permission Id 2 allow 'startVirtualMachine' access for VMs in the 'Domain' scope - VMs in the domainId of the user.
  • In this case, since the VM is in the domain of the user, he is granted access using policy Id 3.

A Root Admin 'admin' calls this command for any VM:

Entity Access Check

  • Find all groups the user belongs to: groupIDs = 2
  • Find all 'Effective' policies the groups are associated to: policies = 2
  • Policy Id 3 and Permission Id 1 allow 'startVirtualMachine' access for ALL VMs .

@ACL annotation changes

  • As illustrated in the above access flow, the access checks get invoked when the resource Ids in the API Cmd are annotated.
  • Thus we will have to edit all existing API Cmds and add the relevant @ACL annotation on the primary resource Ids the command operates on.
  • For any other resources that the command works with, the current access checks placed in the service layer will invoke the SecurityChecker.
  • These current access checks pass the AccessType whereever needed or mostly pass null. Our SecurityChecker will interpret null as a 'Read' access. In the 'acl_permission' schema, all List* APIs will be marked as 'Read' AccessType entries to facilitate this access check.

Creating Custom Group and Policy

Consider following example:

A Domain admin wants to create a 'Service Desk' group for his domain and allow 'ready only' access to the group for all VMs and Volumes within the Domain.

Steps:

  • createAclGroup('Service Desk', 'Service Desk group', $domainId of the admin)
  • createAclPolicy('Read Only Access', 'read only access to domain resources', $domainId of the admin)
  • createAclPermission('ListVirtualMachine', 'Allow', 'Domain', $domainId, 'VirtualMachine')
  • createAclPermission('ListVolumes', 'Allow', 'Domain', $domainId, 'Volume')
  • addAclPermissionToAclPolicy( UUID of the 'Read Only Access' policy, List<String> permissionIds of above permissions)
  • attachAclPolicyToAclGroup (groupId, policyId)
  • addAccountToAclGroup(groupId, List<String> accountIds)

What should be the method of ‘creating a custom Policy/Role’?
For this release, creating a custom policy is supported through API only. For further releases, we can provide either a UI or a config file + policy language mechanism to facilitate the custom policy creation. Gliffy DiagramnameIAM_component_deployment