SPEKTRA Edge Multi-Service Environment Safety

Understanding the SPEKTRA Edge multi-service environment safety.

IAM needs to ensure safety not only between tenants (Organizations, Projects) but also between Services. For this reason RoleBindings are also scoped per Service. There is however a problem with services we need to solve:

  • Organizations and Projects can enable services they use, and if they do, they should be able to use these Services. IAM must ensure that the organization/project admin cannot enable services if they don’t have permission to. IAM must ensure the service does not have access to organizations/projects which don’t enable a particular service.
  • If the Organization or Project enables Service, then the Service should be able to access the Project/Organization.
  • Ideally, the Service should be able to freely access all resources a Project or Organization has, as long as those resources are defined by that service. For example, service devices.edgelq.com must always be able to access any instance of devices.edgelq.com/Device. However, other services should have limited access to devices.edgelq.com/Device collection. It’s called limited access to Projects/Organizations: Project/org admins should be able to regulate which resources are accessible.
  • Some Services may be “private”.
  • If services are using/importing each other, they need some limited access to each other.

Private services are protected by attach permissions, so project/org admins can’t just enable any Service, this requires updating the list of references of Services after all.

Those things are fixed in IAM fixtures, see iam/fixtures/v1/iam_roles.yaml.

First, see this role: services/iam.edgelq.com/roles/service-user. This gives access to Service data and the ability to attach it. If the project admin is granted this role in a service scope, they can enable that service.

Then, the next thing, Service should be able to access all relevant resources projects or organizations have, without specifying the exact instance. This is why we have the IAM role services/iam.edgelq.com/roles/base-edgelq-service, which grants access to all resources across orgs/projects, as long as certain conditions are met. Note that we don’t give any create permissions, it would be wrong, because the Service could start creating resources with the proper metadata.services field, without checking if the project/org even uses the service. It is not an issue for non-creating permissions. To allow services creating project/organization scope resources, we have services/iam.edgelq.com/roles/service-to-project-access and services/iam.edgelq.com/roles/service-to-org-access roles. RoleBindings for these roles are created dynamically by the IAM controller when the Project/Organization enables some service. This code is located in iam/controller/v1/iam_scope.

We also need to regulate service-to-service access. By default, this is not allowed. However, if one service imports or uses another, we enable their access to each other. Roles for these scenarios are in iam/fixtures/v1/per_service_roles.yaml. Roles with ID importing-service-access and imported-service-access are granted to importing and imported service, but note it is not symmetrical. It does not need to be. For example, if one service imports another, then EstablishReferences is only needed in one direction. Roles with ID service-to-service-std-access are used for minimal standard access.

All those RoleBindings regulating access between services, and between services with projects/organizations, are called “Service RoleBindings”. They are dynamically created by the IAM Controller when a service is created/updated, or when an organization/project enables some service. The module responsible for these RoleBindings is in file iam/controller/v1/iam_scope/service_rbs_syncer.go:

  • makeDesiredRbsForSvc computes desired Service RoleBindings per each Service.
  • makeDesiredRbsForOrg computes desired Service RoleBindings per each Organization.
  • makeDesiredRbsForProject computes desired Service RoleBindings per each Project.

Note the convention for mixin services, each service has its copy of them, like services/<service>/permissions/resourceShadows.listMetaOwnees. This is because all services have their schema mixins. Note that RoleBindings for those “per service roles” are located on the root scope, see function makeDesiredRbsForSvc in file iam/controller/v1/iam_scope/service_rbs_syncer.go. The reason is that ResourceShadow is a “root” resource (name pattern is resourceShadows/{resourceShadow}, not something like services/{service}/resourceShadows/{resourceShadow}). Perhaps it could have been like this, but it is some continuity from v1alpha2. Also, CLI commands would become less intuitive. In order then to enable per-service access, permissions are per-service. If we create services/<service>/permissions/resourceShadows.listMetaOwnees per service, and create root scope RoleBinding containing this permission, in effect it will be granted for specific services only, not for all.