Understanding RBAC in Sensu Go

Regulating access to resources is a fundamental measure for enterprises to ensure the security and reliability of a system. The last thing you need is a stolen or weak password to give up the keys to the proverbial kingdom. With role-based access control (RBAC), this risk is mitigated by providing only the necessary access so a user in your organization doesn’t have more access than needed.

What is RBAC?

RBAC is essentially an authorization mechanism that decides which actions users can perform against resources. This control mechanism lets you assign users, or groups of users to roles. Each role represents a collection of permissions to perform certain operations. For example, a role could be defined to include the permission to read and list a specific resource.

Implementing RBAC reduces the security liability of data getting compromised by decreasing the amount of data accessible by a given user. A well-implemented RBAC will be transparent to the users since they will have access to the right data they need to do their job (and no more).

In addition to enhancing security, RBAC also facilitates administration of security in your organization, by maximizing operational efficiency. By creating roles for various job functions, users no longer require personalized permissions. Managing new users can be time consuming and complex, but if you already have roles in place, it’s almost a fire and forget situation.

Lastly, RBAC opens the door to multi-tenancy, in which a single instance of a software serves multiple customers. With a granular RBAC implementation, each customer is logically isolated, but physically integrated. This represents an economical solution because software, hardware and maintenance costs are shared across all customers.

RBAC in the old world

Role-based access control mechanisms have typically been reserved to commercial solutions or are fairly limited in terms of functionality for traditional monitoring solutions.

It is not necessary to look very far to have an example of that, since RBAC has been part of our Sensu Enterprise 3.x offering within the enterprise dashboard.

You get RBAC. Everyone gets RBAC!

60qUiGtAxOZJ69CDH7Gcl5GZ1pdpYWcbuvfL WKgLS0ncAcOSfyXC8ljJUC4yFeG1NrhSe06TAXlVH nxZ8Gy8GKS -Fj8wzfXAcDDA7IEMJnfW4poYBYmik-1oXyXtIEl2T3ok4

One of the first ever decisions we made while reimagining Sensu was to consider RBAC as a first-class citizen throughout Sensu Go. In fact, one of the first features we wrote was the RBAC mechanism so it would be a building block of our API architecture.

Consequently, all users of Sensu Go now have access to our powerful yet simple role-based access control; managing access to Sensu resources no longer requires an enterprise subscription.

For larger organizations, external authentication providers such as Active Directory or OpenLDAP, with first-class integration into RBAC, are available in our enterprise offering.

Peas in a pod

Kubernetes users will feel at home with Sensu’s RBAC, which was deliberately inspired from Kubernetes’ implementation. The same concepts and notions apply, so the learning curve should be hopefully minimal. Permissions can be configured using sensuctl or the Sensu API itself and the same top-level types are declared: Role, ClusterRole, RoleBinding, and ClusterRoleBinding.

That being said, we took the opportunity to streamline certain RBAC resources; we certainly do not have the same granularity requirements since the Sensu API footprint is a bit more concise. For example, the apiGroup and apiGroups keys are unnecessary across RBAC resource specifications, which results in simpler resources.

Role and ClusterRole

Central to RBAC is the concept of roles, which are fundamentally just collections of permissions. These permissions are purely additive, so you will not find any deny rules. A role can be limited to a namespace with a Role, or cluster-wide with a ClusterRole.

A Role can only grant access to resources within its namespace. For example, the following Role could be used to grant read access to checks in the default namespace:

{

  "type": "Role",
  "api_version": "core/v2",
  "metadata": {
    "name": "check-reader",
    "namespace": "default"
  },
  "spec": {
    "rules": [
      {
        "resources": ["checks"],
        "verbs": ["get", "list"]
      }

    ]

  }

}

A ClusterRole can grant the same permissions, but supports more flexible patterns and additional permissions for accessing:

  • Cluster-wide resources (e.g., users)
  • Non-resources endpoints (e.g., /health)
  • Namespaced resources (e.g., checks) across a handful or all namespaces

The default ClusterRoles are a great place to get started and use as a reference. To view them:

$ sensuctl cluster-role list

     Name        Rules
─────────────── ───────
admin               2
cluster-admin       1
edit                2
system:agent        1
system:user         2
view                1

And to view details about a specific ClusterRole:

$ sensuctl cluster-role info view
   Verbs                                       Resources 
Resource Names
 ────────── ────────────────────────────────────────────────────────────────────────────────────────────── ────────────────
  get,list assets,checks,entities,extensions,events,filters,handlers,hooks,mutators,silenced,namespaces

RoleBinding and ClusterRoleBinding

Now that we understand how permissions are defined, let’s look at how these permissions can be associated to a user or a group of users with a binding. A binding holds a list of subjects (users and groups) and a reference to a role. Permissions can be granted to a specific namespace with a RoleBinding, or cluster-wide with a ClusterRoleBinding. The following binding strategies are possible.

RoleBinding referencing a Role

A RoleBinding may only reference a Role within the same namespace, since both resources are namespaced. This pattern is useful when certain subjects require unique permissions to a specific namespace. It might however quickly lead to an increased number of RBAC resources to maintain so it should be used sparingly. In the example below, a RoleBinding named sansa-reads-checks is using a Role named check-reader to grant the user sansa the permission to read and list checks in the default namespace.

{
  "type": "RoleBinding",
  "api_version": "core/v2",
  "metadata": {
    "name": "sansa-reads-checks",
    "namespace": "default"
  },
  "spec": {
    "role_ref": {
      "name": "check-reader",
      "type": "Role"
    },
    "subjects": [
      {
        "name": "sansa",
        "type": "User"
      }
    ]
  }
}

ClusterRoleBinding referencing a ClusterRole

A ClusterRoleBinding may reference a ClusterRole, which will result in permissions being granted in all namespaces and, depending on the resources defined in the ClusterRole, to cluster-wide resources. This pattern should be used with caution, especially if your goal is to grant subjects access to only namespaced resources across all namespaces and not cluster-wide resources; in this situation, you will need to explicitly define all granted resources, which will increase its complexity. The following ClusterRoleBinding authorizes any user in the group starks to read and list events in any namespace.

{
  "type": "ClusterRoleBinding",
  "api_version": "core/v2",
  "metadata": {
    "name": "starks-read-events-global",
  },
  "spec": {
    "role_ref": {
      "name": "event-reader",
      "type": "ClusterRole"
    },
    "subjects": [
      {
        "name": "starks",
        "type": "Group"
      }
    ]
  }
}

RoleBinding referencing a ClusterRole

Additionally, a RoleBinding may reference a ClusterRole, which produces a powerful pattern. All permissions defined in the ClusterRole will be granted to namespaced resources within the RoleBinding's namespace. Using this strategy, a set of common permissions can be defined within a single ClusterRole and then reused to target multiple namespaces, by using a RoleBinding for each namespace. In the following example, we reuse the same ClusterRole from our previous example, but this time we use a RoleBinding so the permissions are only applied to the development namespace.

{
  "type": "RoleBinding",
  "api_version": "core/v2",
  "metadata": {
    "name": "jon-reads-events",
    "namespace": "development"
  },
  "spec": {
    "role_ref": {
      "name": "event-reader",
      "type": "ClusterRole"
    },
    "subjects": [
      {
        "name": "jon",
        "type": "User"
      }
    ]
  }
}

Authentication providers + RBAC = 💚

Support for external authentication providers, such as Active Directory or OpenLDAP (enterprise features available in Sensu Go), allows subjects of binding resources to directly reference users and groups that live in the provider, so you can take advantage of your already existing organizational structure. For example, the following ClusterRoleBinding refers straight to an Active Directory group named lannister:

{
  "type": "ClusterRoleBinding",
  "api_version": "core/v2",
  "metadata": {
    "name": "read-events-global",
  },
  "spec": {
    "role_ref": {
      "name": "event-reader",
      "type": "ClusterRole"
    },
    "subjects": [
      {
        "name": "ad:lannister",
        "type": "Group"
      }
    ]
  }
}

Learn more

I hope this blog post gave you a good overview of RBAC in Sensu Go. If you haven’t already, you can download Sensu Go here and give RBAC a try (plus other exciting new features in Sensu Go, including assets). To learn more about RBAC, check out these resources: