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
-
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.
-
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.
-
Q: is this backwards compatible with existing GET deployments?
A: Yes.
-
Q: what changes do GET users who do not want to use this feature need to make?
A: None at all.
-
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:
- None: this is a vanilla service account, intended for GCE VM instances not running the GitLab Rails Codebase, such as Gitaly, Consul, etc.
- GITLAB_APP_VM: this role is used for service accounts created for GCE VM instances running Rails and Sidekiq
- 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.
gitlab_gcp_service_account
Module
Using the 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"
}