Simple User management for single tenant services

Problem

We want to provide easy user management for the AppCat services. The customer should be able to create users, permissions and if available for the given service, databases. The structure of the API should be similar for each service, just like the rest of the AppCat API is similar between services. This decision only concerns itself with services that are single tenant in nature. Cluster-wide services with multi-tenant user management are not part of this decision and will be handled in a separate document.

Goals

  • User management for single tenant services like PostgreSQL, MariaDB, Redis, etc.

  • The user management API should adhere to a convention spanning all services so that it follows the same structure for every service

  • Find a decision for simple service. There might be services in the future that have complex user and RBAC mechanisms that may not fit in this concept and need a custom decision

  • The API should be identical for the same service from different providers, given feature parity of the providers

  • Don’t abstract service specific concepts in the API, this could lead to confusion

Non-Goals

  • Having the exact same API for all services, it’s unrealistic and too abstract

  • User management for cluster-wide multi-tenant systems (for example Minio and potentially Kafka)

Proposals

Generally

Points outlined in this section apply to all proposals.

When choosing a mechanism to manage the users, then we should prefer platform and provider-agnostic solutions before specific ones. For example provider-sql can manage PostgreSQL, MySQL and MSSQL directly. It only needs credentials to connect to the instance. In contrast, passing the user management to StackGres and let it handle the provisioning, would only work for PostgreSQL by VSHN, and we’d still have to come up with a solution for other PostgreSQL providers.

If there are no crossplane providers or other operators to manage a service, but terraform modules are available, we can leverage Crossplane’s UpJet.

When specifying users, then no passwords can be specified, only the names of the users. Any passwords will be generated by AppCat. Each created user should get its own connection secret in the claim namespace.

Proposal 1: Add user management to claims

Each service exposes the user management under the spec.parameters.service field in the claims.

Each service needs to be extended with functions that deploy the following:

  • Create a new secret from connection detail that satisfies the provider

  • Deploy a providerConfig

  • Deploy a managed resource for the provider for each user/database/grant

There’s a POC for this in component-appcat and AppCat.

Example:

apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNPostgreSQL
metadata:
  name: my-pg
spec:
  parameters:
    ...
    service:
      users: (1)
        - prod
        - test
        - int
      databases: (2)
        - prod
        - int
        - test
      grants: (3)
        - privileges:
            - ALL
          type: GRANT
          user: prod
          database: prod
1 Generic for PostgreSQL, MariaDB and potentially more
2 Generic for PostgreSQL and MariaDB
3 PostgreSQL specific, modelled after provider-sql 's CRDs.
Advantages
  • Each service has exactly one source of truth

  • Simple to understand for the end-user, permissions and security can be defined right in the claim

Disadvantages
  • A claim could get very large

Proposal 2: User management separate of claims

Each service has additional XRDs that manage the user and permissions for the given service.

There are two variations how this could be done. Having a reference to the instance in the user management XRD. Or Having a reference for the user management XRD in the main claim.

The complexity of this solution will be much higher:

  • Each service needs a new XRD

  • Each of these XRDs need a new composition

  • Each of these XRDs need a new composition function

  • The function itself first needs to resolve the references before it can actually deploy the necessary configurations for the provider

Example reference on management object:

apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNPostgreSQL
metadata:
  name: my-pg
spec:
  parameters: {}
---
apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNPostgreSQLIAM
metadata:
  name: myiam
spec:
  parameters:
    postgreSQLClaimRef:
      name: my-pg
    service:
      users: (1)
        - prod
        - test
        - int
      databases: (2)
        - prod
        - int
        - test
      grants: (3)
        - privileges:
            - ALL
          type: GRANT
          user: prod
          database: prod
1 Generic for PostgreSQL, MariaDB and potentially more
2 Generic for PostgreSQL and MariaDB
3 PostgreSQL specific, modelled after provider-sql 's CRDs.

Example reference on claim:

apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNPostgreSQL
metadata:
  name: my-pg
spec:
  parameters:
    service:
      iamRefs:
        - myiam
---
apiVersion: vshn.appcat.vshn.io/v1
kind: VSHNPostgreSQLIAM
metadata:
  name: myiam
spec:
  parameters:
    users: (1)
      - prod
      - test
      - int
    databases: (2)
      - prod
      - int
      - test
    grants: (3)
      - privileges:
          - ALL
        type: GRANT
        user: prod
        database: prod
1 Generic for PostgreSQL, MariaDB and potentially more
2 Generic for PostgreSQL and MariaDB
3 PostgreSQL specific, modelled after provider-sql 's CRDs.
Advantages
  • The claims don’t get inflated

Disadvantages
  • More room for the customer to make mistakes by referencing wrong instances

  • Multiple claims reconcile the same service, no single point of truth

  • Complexity increases a lot

  • Deletion can get messy, what happens if the claim gets deleted, but the users, databases and grant objects still exist?

Decision

Proposal 1.

Rationale

Adding the user management to the claim is the most straight forward solution. Managing the users in a separate XRD increases the complexity by a lot. It requires separate compositions and composition functions to be able to work. It also makes it harder for the end-user to use. They have to make sure that the references are correct, which can be annoying.