Skip to content

support: allow service accounts to be passed into GET

NOTE: targets 3.0.x. For the main version of this patch, see !1227 (merged).

Required for https://gitlab.com/gitlab-com/gl-infra/gitlab-dedicated/instrumentor/-/merge_requests/2442

Overview

This change allows for the creation of GET GCP IAM Service Accounts separately from the Provisioning process.

FAQ

  1. Q: why do you need this?

    A: In some environments, we want to enforce a separation between the creation of admin level privileges and run-of-the-mill provisioning privileges. This can help prevent privilege escalation.

  2. Q: Does the design of IAM in GCP affect this?

    A: Yes, very much. In GCP, policies can only be defined for resources (eg, service accounts) that exist. This means that trust relationships need to be defined after the service accounts are created. If the service accounts are only being defined in the provision stage, many of the trust relationships will need to be defined after that, requiring escalated privileges in the provision stage. This is something that the Dedicated Team are very opposed to allowing, for reasons of security, operational safety and principal of least privilege.

  3. Q: is this backwards compatible with existing GET deployments?

    A: Yes.

  4. Q: what changes do GET users who do not want to use this feature need to make?

    A: None at all.

  5. Q: how will GET users be able to ensure that future updates to IAM membership are adopted?

    A: GET users that provision the GET service accounts still use a GET provisioned module and will receive all updates. More details below.

Service Account Roles

This module introduces a new Terraform module, gitlab_gcp_service_account and extracts the existing Service Module creation into this module.

This module is then plumbed back into the original locations that Service Accounts were being used, to ensure backwards compatibility with existing clients.

However, since the module has been extracted out of the main module, clients can use it to create service accounts outside of the provisioning process.

The module uses the concept of "roles" for service accounts. Based on the existing code-base, there are three possible roles:

  1. None: this is a vanilla service account, intended for GCE VM instances not running the GitLab Rails Codebase, such as Gitaly, Consul, etc.
  2. GITLAB_APP_VM: this role is used for service accounts created for GCE VM instances running Rails and Sidekiq
  3. GKE_NODE_POOL: this role is used for service accounts created for running k8s node pool VM instances

These three roles cover all the current scenarios in GET. The scheme supports gracefully adding new roles in future.

Using the gitlab_gcp_service_account Module

The module's functionality is very similar to the basic google_service_account, except that it also supports "switching" between creating the resource and simply querying it as a data resource. To switch from create mode to query mode, the provided_service_account_id should be configured with an existing service account, created by a separate process.

In normal operation, the module will default to creation mode, but will switch into query mode when provided provided_service_account_id input from the caller.

In both modes, the outputs from the module are identical. This means that downstream consumers of google_service_account are unaffected by whether the module is in create mode or query mode, reducing complexity in adding this change.

For advanced GET users, such as Dedicated, there's a preference to create the service accounts using a more privileged (and more scrutinized) stage of our provisioning process (called "Onboard" stage).

Custom IAM Service Accounts options (GCP)

By default, GET will create IAM Service Accounts in GCP. These service accounts are used for IAM instances, Kubernetes nodepools etc.

Alternatively, you may wish to create the Service Accounts outside of GET and configure GET to use the Service Accounts of your choosing.

This can be done using the node_type_service_account_emails (for GCE instances) and node_pool_service_account_emails (for GKE nodepools).

  node_type_service_account_emails = {
    # All keys are optional. Any missing service accounts will be created by GET
    consul            = "my-service-account-consul@${var.gcp_project_id}.iam.gserviceaccount.com"
    gitaly            = "my-service-account-gitaly@${var.gcp_project_id}.iam.gserviceaccount.com"
    gitlab-nfs        = "my-service-account-gitlab-nfs@${var.gcp_project_id}.iam.gserviceaccount.com"
    gitlab-rails      = "my-service-account-gitlab-rails@${var.gcp_project_id}.iam.gserviceaccount.com"
    haproxy-external  = "my-service-account-haproxy-external@${var.gcp_project_id}.iam.gserviceaccount.com"
    haproxy-internal  = "my-service-account-haproxy-internal@${var.gcp_project_id}.iam.gserviceaccount.com"
    monitor           = "my-service-account-monitor@${var.gcp_project_id}.iam.gserviceaccount.com"
    opensearch        = "my-service-account-opensearch@${var.gcp_project_id}.iam.gserviceaccount.com"
    pgbouncer         = "my-service-account-pgbouncer@${var.gcp_project_id}.iam.gserviceaccount.com"
    postgres          = "my-service-account-postgres@${var.gcp_project_id}.iam.gserviceaccount.com"
    praefect          = "my-service-account-praefect@${var.gcp_project_id}.iam.gserviceaccount.com"
    praefect-postgres = "my-service-account-praefect-postgres@${var.gcp_project_id}.iam.gserviceaccount.com"
    redis             = "my-service-account-redis@${var.gcp_project_id}.iam.gserviceaccount.com"
    redis-cache       = "my-service-account-redis-cache@${var.gcp_project_id}.iam.gserviceaccount.com"
    redis-persistent  = "my-service-account-redis-persistent@${var.gcp_project_id}.iam.gserviceaccount.com"
    sidekiq           = "my-service-account-sidekiq@${var.gcp_project_id}.iam.gserviceaccount.com"
  }

  node_pool_service_account_emails = {
    # All keys are optional. Any missing service accounts will be created by GET
    webservice = "my-service-account-gke-webservice@${var.gcp_project_id}.iam.gserviceaccount.com"
    sidekiq    = "my-service-account-gke-sidekiq@${var.gcp_project_id}.iam.gserviceaccount.com"
    supporting = "my-service-account-gke-supporting@${var.gcp_project_id}.iam.gserviceaccount.com"
  }

cc @davidremmer-ext

Edited by Andrew Newdigate

Merge request reports