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.