Saltar al contenido principal
Página traducida

Esta página se tradujo del inglés con ayuda de IA. Si hay diferencias entre ambas, la versión en inglés es la referencia.

Framework de GitOps para vinculaciones de IAM de Google con StackQL

En esta guía mostraremos un framework de GitOps para la gestión de vinculaciones de IAM (derechos de acceso) en GCP con StackQL, una potente herramienta de desarrollo que permite consultar y desplegar infraestructura y recursos en la nube con sintaxis SQL.

Probado con backend SQL integrado macos linux powershell

Contexto de IAM de Google​

Google Cloud Identity and Access Management (IAM) es un marco para gestionar el acceso a los recursos de Google Cloud que permite a los administradores definir quién puede realizar acciones específicas sobre los recursos. Se basa en identidades (como usuarios y cuentas de servicio), roles y permisos, de modo que los roles asignados a estas identidades determinan sus derechos de acceso. Esta guía profundiza en la gestión de las vinculaciones de IAM, que asocian estos roles a las identidades, con StackQL, una herramienta que simplifica la gestión de recursos en la nube con sintaxis SQL.

Las vinculaciones de IAM se pueden crear en cualquiera de los siguientes niveles:

  • nivel de organización
  • nivel de carpeta
  • nivel de proyecto
  • nivel de recurso (buckets, conjuntos de datos de BigQuery, etc.)
  • nivel de cuenta de facturación

Todos los objetos secundarios heredan las vinculaciones de IAM aplicadas a nivel de organización o de carpeta.

Definiciones declarativas de vinculaciones y GitOps​

La guía le mostrará cómo mantener la copia maestra de las vinculaciones de IAM en su repositorio de código fuente, de modo que este sea la fuente de verdad del estado y del historial (un concepto conocido como GitOps), y cómo usar StackQL para aplicar cambios con workflows de CI/CD.

Este ejemplo le muestra cómo asociar muchos miembros o principales a muchos roles a partir de un manifiesto json almacenado en el control de código fuente.

La consola de GCP y las utilidades terraform y gcloud solo permiten asociar un miembro a uno o más roles en cada operación

Funcionamiento​

El framework que le presentaremos paso a paso desplegará vinculaciones de IAM en todos los nodos de su jerarquía de GCP y también creará roles personalizados (con permisos seleccionados) - a diferencia de los roles predefinidos, como roles/bigquery.dataEditor, por ejemplo.

Estructura de directorios del proyecto​

Este framework de despliegue de vinculaciones usa el siguiente árbol de directorios:

├── iql
│ ├── iam.iql
│ ├── ...
├── data
│ ├── iam.jsonnet
│ ├── iam
│ │ ├── bindings.json
│ │ ├── custom_roles.json

Archivo iql/iam.iql​

Este es el archivo principal que ejecutará stackql; con data/iam.jsonnet, renderiza y aplica las vinculaciones de políticas transpiladas que se generan a partir de los datos de data/iam/bindings.json

no debería ser necesario modificar este archivo

Expanda para ver el archivo iql/iam.iql
/*
ROOT LEVEL (ORG AND FOLDER) IAM FOR SERVICE ACCOUNTS AND GROUPS
*/
{{ $root := . }}

/*** create custom roles ***/ {{ range .custom_roles }}

INSERT INTO google.iam.`organizations.roles`(
parent,
data__role,
data__roleId )
SELECT
'{{ $root.organization_id }}',
'{"title": "{{ .title }}", "description": "{{ .description }}", "stage": "{{ .stage }}", "includedPermissions": {{ .includedPermissions }}}',
'{{ .id }}'
;{{ end }}

/*** create org role bindings */

EXEC google.cloudresourcemanager.organizations.setIamPolicy
@resource = '{{ $root.organization_id }}'
@@json = '{
"policy": {
"bindings": {{ $root.bindings.org }}
}
}';

/*** create billing role bindings */

EXEC google.cloudbilling.billingAccounts.setIamPolicy
@resource = '{{ $root.billing_account_id }}'
@@json = '{
"policy": {
"bindings": {{ $root.bindings.billingacct }}
}
}';

/*** create nonprod folder role bindings */

EXEC google.cloudresourcemanager.folders.setIamPolicy
@resource = '{{ $root.nonprod_folder_id }}'
@@json = '{
"policy": {
"bindings": {{ $root.bindings.folders.nonprod }}
}
}';

/*** create prod folder role bindings */

EXEC google.cloudresourcemanager.folders.setIamPolicy
@resource = '{{ $root.prod_folder_id }}'
@@json = '{
"policy": {
"bindings": {{ $root.bindings.folders.prod }}
}
}';

/*** create datalabs folder role bindings */

EXEC google.cloudresourcemanager.folders.setIamPolicy
@resource = '{{ $root.datalabs_folder_id }}'
@@json = '{
"policy": {
"bindings": {{ $root.bindings.folders.datalabs }}
}
}';

/*** create stackql-audit project level role bindings */

EXEC google.cloudresourcemanager.folders.setIamPolicy
@resource = 'projects/stackql-audit'
@@json = '{
"policy": {
"bindings": {{ $root.bindings.projects.stackql_audit }}
}
}';

/*** create role bindings for buckets in stackql-terraform */ {{ range $root.bindings.buckets }}

-- creating policy bindings for {{ .name }}
EXEC google.storage.buckets.setIamPolicy
@bucket = '{{ .name }}'
@@json = '{
"bindings": {{ .data }}
}';
{{ end }}

/*** create role bindings for topics in stackql-audit */ {{ range $root.bindings.topics }}

-- creating policy bindings for {{ .name }}
EXEC google.pubsub.`projects.topics`.setIamPolicy
@resource = '{{ .resource }}'
@@json = '{
"bindings": {{ .data }}
}';
{{ end }}

data/iam.jsonnet​

iql/iam.iql preprocesa este archivo con los datos de data/iam/bindings.json

no debería ser necesario modificar este archivo

Expanda para ver el archivo data/iam.jsonnet
// update the following files only:
// data/iam/custom_roles.json
// data/iam/bindings.json

// variables
local custom_roles_data = import './data/iam/custom_roles.json';
local bindings_data = import './data/iam/bindings.json';
local organization_id = 'organizations/123466304837';
local billing_account_id = 'billingAccounts/123456-9DFD97-EE2B33';
local nonprod_folder_id = 'folders/1234016945998';
local prod_folder_id = 'folders/123431606453';
local datalabs_folder_id = 'folders/123464988355';
local environments = [{name: 'prod'}, {name: 'nonprod'}, {name: 'datalabs'}];

// DO NOT MODIFY BEYOND THIS POINT

local generate_binding(x) =
local members = [x for x in x.members];
std.map(function(r) {"role": r, "members": members} , [x for x in x.roles]);

local generate_conditional_binding(x) =
local members = [x for x in x.members];
local condition = x.condition;
std.map(function(r) {"role": r, "members": members, "condition": condition} , [x for x in x.roles]);

{
organization_id: organization_id,
billing_account_id: billing_account_id,
nonprod_folder_id: nonprod_folder_id,
prod_folder_id: prod_folder_id,
datalabs_folder_id: datalabs_folder_id,
custom_roles: custom_roles_data,
environments: environments,
bindings: {
org: std.flattenArrays(std.map(generate_binding, bindings_data.org)),
billingacct: std.flattenArrays(std.map(generate_binding, bindings_data.billingacct)),
folders: {
nonprod: std.flattenArrays(std.map(generate_binding, bindings_data.folders.nonprod)),
prod: std.flattenArrays(std.map(generate_binding, bindings_data.folders.prod)),
datalabs: std.flattenArrays(std.map(generate_binding, bindings_data.folders.datalabs)),
},
projects: {
stackql_audit: std.flattenArrays(std.map(generate_conditional_binding, bindings_data.projects.stackql_audit)),
},
buckets: [
{
name: "stackql-tf-prod",
data: std.flattenArrays(std.map(generate_binding, bindings_data.buckets.stackql_tf_prod)),
},
{
name: "stackql-tf-modules-prod",
data: std.flattenArrays(std.map(generate_binding, bindings_data.buckets.stackql_tf_modules_prod)),
},
{
name: "stackql-tf-nonprod",
data: std.flattenArrays(std.map(generate_binding, bindings_data.buckets.stackql_tf_nonprod)),
},
{
name: "stackql-tf-modules-nonprod",
data: std.flattenArrays(std.map(generate_binding, bindings_data.buckets.stackql_tf_modules_nonprod)),
},
{
name: "stackql-tf-datalabs",
data: std.flattenArrays(std.map(generate_binding, bindings_data.buckets.stackql_tf_datalabs)),
},
{
name: "stackql-tf-modules-datalabs",
data: std.flattenArrays(std.map(generate_binding, bindings_data.buckets.stackql_tf_modules_datalabs)),
},
],
topics: [
{
name: "stackql-np-log-topic",
resource: "projects/stackql-audit/topics/stackql-np-log-topic",
data: std.flattenArrays(std.map(generate_binding, bindings_data.topics.stackql_np_log_topic)),
},
{
name: "stackql-prod-log-topic",
resource: "projects/stackql-audit/topics/stackql-prod-log-topic",
data: std.flattenArrays(std.map(generate_binding, bindings_data.topics.stackql_prod_log_topic)),
},
],
},
}

data/iam/bindings.json​

será necesario modificar este archivo para cambiar las vinculaciones de políticas

Esta es la fuente maestra de los datos de políticas y vinculaciones; la estructura de datos asocia uno o varios principales (grupos, cuentas de servicio, etc.) a uno o varios roles (predefinidos o personalizados) en un nivel concreto (el elemento superior), como se muestra a continuación:

"<< level >>": [
{
"members": [
"<< principal >>",
...
],
"roles": [
"<< role >>",
...
]
},

por ejemplo:

"folders": {
"nonprod": [
{
"members": [
"serviceAccount:terraform-nonprod@stackql-terraform.iam.gserviceaccount.com"
],
"roles": [
"roles/compute.networkAdmin",
"roles/resourcemanager.folderViewer",
"roles/resourcemanager.projectCreator",
"roles/storage.admin",
"roles/artifactregistry.admin",
"roles/container.serviceAgent",
"roles/iam.securityAdmin",
"roles/bigquery.admin",
"roles/serviceusage.serviceUsageAdmin",
"roles/cloudsql.admin",
"roles/iam.serviceAccountAdmin",
"roles/pubsub.admin",
"roles/cloudfunctions.developer"
]
}
],

Despliegue​

Intencionadamente, este proceso no se ejecuta en un pipeline de CI/CD, ya que los cambios son poco frecuentes y no queremos conceder a una cuenta de servicio el nivel de privilegios necesario para desplegarlo. Debe ejecutarlo un administrador con privilegios que sea miembro del grupo de Google gcp-org-administrators.

El usuario debe estar autenticado en GCP, ya sea con una cuenta de servicio o con autenticación interactiva; para obtener más información sobre la autenticación de StackQL en Google, consulte Autenticación en GCP.

Ejecute el siguiente comando para realizar una ejecución de prueba:

stackql exec -i ./iql/iam.iql \
--iqldata ./data/iam.jsonnet \
--outfile iam-TEMPLATED.iql \
--dryrun --output text --hideheaders

Los datos resultantes de este archivo se pueden ejecutar como instrucciones individuales en stackql shell o como un lote, con el siguiente comando:

stackql exec -i iam-TEMPLATED.iql \

Como alternativa, puede tomar las políticas (cargas útiles JSON) que genera este script y ejecutarlas con el comando gcloud iam.