IAM

Identity and Access Management service (IAM) is the Kaa platform component that allows you to manage users, groups, and their permissions to access Kaa resources. Permissions are evaluated using policies that allow or deny actions on resources for principals: users and groups.

Policy

Let’s start reviewing IAM concepts with a cornerstone entity called policy.

Policy is, generally speaking, a combination of principals (users and groups), resources (e.g. applications, endpoints, users, groups, etc.), possible actions on resources (e.g. read, update, execute a command, delete), and effects (allow or deny). Its main responsibility is to permit or decline particular operations on specific resources for a given authenticated principal.

Policies can be represented with JSON documents containing several fields that we will describe later.

IAM supports two policy types: identity- and resource-based. The key difference between the two is that resource-based policies are always associated with a single resource using a 1-1 relation. Let’s review both types in more detail.

Identity-based policy

Identity-based policies are generic policies that define what actions can be performed on what resources by which principals. You can reuse identity-based policies to grant permissions to multiple principals via policy attachment. Additionally, multiple resource KRNs can be specified for bundling permission grants.

Let’s start with an example statement: user Alice (principal) can read data (action) from a thermostat (resource).

Have a look at the policy JSON representation for the above statement.

{
  "name": "thermostat-X-policy",
  "type": "identity",
  "description": "Allow Alice to read thermostat data",
  "statements": [
    {
      "effect": "allow",
      "actions": [
        "endpoint:read"
      ],
      "principals": [
        "krn:iam:3fdfde93-661a-47d1-abcc-68452dd320c7::user/alice"
      ],
      "resources": [
        "krn:epr:3fdfde93-661a-47d1-abcc-68452dd320c7::endpoint/5766b7e9-1f16-443d-8e4a-553f70733aa7"
      ],
      "description": "Example statement description"
    }
  ]
}

You don’t have to immediately understand all the JSON fields; We’ll talk about them later. What’s important for now is that identity-based policy statements have "principals" and "resources" fields. "principals" list all principal KRNs (user with Alice username here), while "resources" list all resource KRNs (5766b7e9-1f16-443d-8e4a-553f70733aa7 endpoint ID) that the policy statement applies to. Principals are represented by users and groups.

KRN stands for Kaa Resource Name. For now, think about KRN as of entity’s fully qualified name (FQN). Later we will discuss KRNs in more detail.

Depending on the resource KRNs, identity-based policies can have a global or a tenant-wide scope. Global policies support KRNs and KRN wildcards addressing entities across multiple tenants. Such policies can’t be created via the {service_name} API, but only from the service configuration. Accordingly, tenant-wide policies can only reference KRNs that belong to a single tenant.

Resource-based policy

Resource-based policies allow managing access to individual resources. One such policy is automatically created by IAM for each resource created by the resource server. You can’t reassign or scale this policy to include multiple resources, but this type of policy provides a clear method for granular access control to an individual resource.

Additionally, resource-based policies support granting permissions to principals across tenants.

Let’s continue with the example of Alice and thermostats. Imagine that Alice has integrated a thermostat but some data does not look right, so she seeks assistance from her Kaa instance administrators. With a resource-based policy, Alice can grant read access to the device data to the administrator group from the Kaa built-in tenant kaa and get assistance.

This is how such policy would look like as a JSON document:

{
  "name": "krn:epr:3fdfde93-661a-47d1-abcc-68452dd320c7::endpoint/5766b7e9-1f16-443d-8e4a-553f70733aa7",
  "type": "resource",
  "description": "Individual resource policy",
  "statements": [
    {
      "effect": "allow",
      "actions": [
        "endpoint:read"
      ],
      "principals": [
        "krn:iam:3fdfde93-661a-47d1-abcc-68452dd320c7::user/alice",
        "krn:iam:kaa::group/administrators"
      ],
      "description": "Example statement description"
    }
  ]
}

You can notice that principals listed in the policy statement refer to two tenants: Alice’s “home” tenant 3fdfde93-661a-47d1-abcc-68452dd320c7 and the built-in tenant kaa. You will find out about the KRNs structure in more detail soon.

You can also see that a resource-based policy does not have the resources statement field. Since every policy is by definition related to only one resource you can think of the resources field as implicit and always containing only one resource KRN.

Policy structure

Now it’s time to review the policy fields.

  • name is the short name of the policy. Identity-based policy name must be non-empty, unique tenant-wide, and only contain latin letters (a-zA-Z), digits (0-9), dashes (-), and underscores (_). Resource-based policy names are automatically assigned by {service_name} to match the KRN of the corresponding resource, and they also include separators : and /.

  • type is a JSON string that defines whether it is a identity-based or resource-based policy. Acceptable values for this field are "identity" for the former and "resource" for the latter.

  • description is a human-readable policy metadata. You can use it to convey the meaning of the policy to the reader so they don’t have to decipher the entire policy definition.

  • statements is an array of policy allow and deny statements.

    • effect is a string that can have only two values: "allow" or "deny". As you will shortly see from the policy evaluation section, IAM always starts with an implicit deny, so at least one allow policy statement must exist for a principal to be permitted an action on a resource.

    • actions is an array of actions that can be performed on resources. Each resource service has its own set of actions applicable to resources they manage. In other words, actions are operations that can be performed on resources. Actions typically consist of resource type, optional resource sub-type, and operation divided by semicolons (:): <resource-type>:<resource-sub-type>:<operation>. Action subtokens may contain lowercase latin letters (a-z) and dashes (-). For example, application:endpoint-filter:create. Additionally, actions support a wildcard asterisk character (*), which can be used either on it’s own to match any actions, or as the last character immediately following a semicolon delimiter to match all actions with a given prefix. For example, * matches all possible resource actions, and application:endpoint-filter:* matches all endpoint filter actions.

    As a nod to the JWT scopes concept, term “scope” can sometimes be used interchangeably with “action” in the Kaa and IAM. For more information on scopes supported in the Kaa platform, refer to this documentation.

    • principals is an array of user and/or group KRNs that the given statement applies to. Principals act as action initiators, and the policy statement defines whether to allow or deny such actions. Again, for now, think about user/group KRN as of their fully qualified name (FQN).

    • resources is an array of resource KRNs that the statement applies to. This field is explicitly defined in identity-based policies, but implicitly matches the corresponding resource in resource-based ones.

    • description is a human-readable statement metadata.

Policy evaluation

Let’s review the IAM policy evaluation flow to see how IAM decides whether to allow or deny operation on a resource for a principal.

IAM policy evaluation flow

IAM implicitly assumes any decision to be a deny unless it encounters a policy that allows the operation. An allow decision is made only if there is a matching allow statement and no matching deny statements.

This conceptually simple behavior allows constructing policy combinations to represent even the most sophisticated business rules for scenarios of arbitrary complexity while retaining the desired level of access granularity. Remember that you can use both user and group KRNs as principals. Moreover, you can use resource KRN wildcards: more on these in a bit.

Kaa Resource Name (KRN)

Kaa Resource Name (KRN) is a fully qualified name string that uniquely identifies a resource or a principal.

KRNs have the following format:

krn:<service>:<tenant ID>:[<pool>]:<resource type>[<resource path>]/<resource ID>

Let us review the KRN tokens in more detail.

  • krn—static prefix used to visually identify KRNs among other identifier strings.
  • <service>—short identifier of the resource server that owns the resource. For example: epr, wd, [iam][IAM], etc.
  • <tenant ID>—unique tenant identifier. Usually tenant ID is a UUID, but every Kaa installation also has one system tenant ID kaa. Examples: kaa, 3fdfde93-661a-47d1-abcc-68452dd320c7, etc.
  • <pool>—optional pool that the resource belongs to. Pools provide resource isolation within tenants. Further, pools may be nested, in which case the complete hierarchy must be specified in the KRN, starting with the root pool and using slash (/) as a separator. Pool nesting is a powerful concept that can be used for representing large, hierarchical organizations, or even third parties such as distributors, regional offices, etc. For example: my-division, my-division/my-subdivision, my-division/my-subdivision/my-sub-subdivision, etc.

Pools are not supported in this version of the {service_name}. This KRN token is reserved and must remain empty for all resources.

  • <resource type>—type of the resource identified by the KRN, e.g. endpoint, user, application, and others.
  • <resource path>—optional path the resource resides in, as defined at the discretion of the resource server. Path can be nested, typically to represent a physical or logical location of the resource. If present, the resource path must be non-empty and start with a slash (/). In case of a nested path, this token becomes composite with sub-tokens also separated by slashes. For example: /floor-1, /floor-1/room-3, etc.
  • <resource ID>—unique resource identifier as defined by the resource server. This may be an endpoint ID, username, application name, etc. For example: 5766b7e9-1f16-443d-8e4a-553f70733aa7, alice, etc.

Let’s have a look at a complete KRN. You should now be able to identify its constituent tokens and understand their meaning.

krn:epr:3fdfde93-661a-47d1-abcc-68452dd320c7:my-division:endpoint/floor-1/5766b7e9-1f16-443d-8e4a-553f70733aa7
     ^                    ^                     ^           ^          ^                      ^
  service             tenant ID                pool    resource type    resource path      resource ID

KRN (sub-)tokens consist of latin letters (a-zA-Z), digits (0-9), dashes (-), underscores (_), at signs (@), and dots (.). Semicolon (:) and slash (/) are reserved separators for KRN tokens and subtokens, respectively. Finally, asterisk (*) is reserved for wildcard KRNs, which is our next subject.

KRN wildcards

As you have seen from IAM policy structure, KRNs play an important role in policy definition. To make it easier to create reusable policies, IAM supports wildcards in policy KRNs.

To define a wildcard KRN, use the special asterisk sign (*). There are several rules for valid wildcard KRNs:

  • only one asterisk is allowed
  • asterisk must be the last character
  • asterisk must follow a KRN token or subtoken delimiter (: or /, respectively), except in the * blanket KRN that matches everything

Let’s review a few examples of KRN wildcards:

  • *—matches any KRN
  • krn:*—also, effectively, matches any well-formed KRN
  • krn:epr:*—matches KRNs of any resources managed by EPR
  • krn:epr:3fdfde93-661a-47d1-abcc-68452dd320c7:*—matches KRNs of any EPR resources in tenant 3fdfde93-661a-47d1-abcc-68452dd320c7
  • krn:iam:3fdfde93-661a-47d1-abcc-68452dd320c7::user/*—matches any IAM users in tenant 3fdfde93-661a-47d1-abcc-68452dd320c7
  • krn:iam:3fdfde93-661a-47d1-abcc-68452dd320c7::user/divisionA/*—matches any IAM users in tenant 3fdfde93-661a-47d1-abcc-68452dd320c7 under the divisionA path, including nested paths

By using KRN wildcards to define policy statements, you can dramatically reduce the number of needed policies and and their management requirements.

Interfaces

IAM supports a number of interfaces to perform its functional role. The key supported interfaces are summarized in the following diagram.

IAM interfaces diagram

For inter-service communication, Kaa services mainly use REST APIs and messaging protocols that run over NATS messaging system.

Management interface

IAM exposes an HTTP-based management interface with the following endpoints:

  • GET /health returns 200 OK if the service is up and running properly, and 500 Internal Server Error otherwise. In case of errors, the response payload contains their human-redable descriptions. This endpoint can be used by Kubernetes for liveness and readiness probing.
  • GET /metrics provides service metrics in Prometheus text-based format.