Diese Seite wurde mit KI-Unterstützung aus dem Englischen übersetzt. Bei Abweichungen ist die englische Version maßgeblich.
GitOps-Framework für Google-IAM-Bindings mit StackQL
In dieser Anleitung stellen wir ein GitOps-Framework für die Verwaltung von IAM-Bindings (Berechtigungen) in GCP mit StackQL vor, einem leistungsfähigen Entwicklerwerkzeug, mit dem sich Cloud-Infrastruktur und -Ressourcen per SQL-Syntax abfragen und bereitstellen lassen.
Getestet mit eingebettetes SQL-Backend macos linux powershell
Hintergrund zu Google IAM
Google Cloud Identity and Access Management (IAM) ist ein Framework zur Verwaltung des Zugriffs auf Google-Cloud-Ressourcen, mit dem Administratoren festlegen, wer welche Aktionen auf Ressourcen ausführen darf. Es basiert auf Identitäten (wie Benutzern und Dienstkonten), Rollen und Berechtigungen, wobei die diesen Identitäten zugewiesenen Rollen deren Zugriffsrechte bestimmen. Diese Anleitung befasst sich mit der Verwaltung von IAM-Bindings, die diese Rollen mit Identitäten verknüpfen, mithilfe von StackQL, einem Tool, das die Verwaltung von Cloud-Ressourcen per SQL-Syntax vereinfacht.
IAM-Bindings können auf jeder der folgenden Ebenen erstellt werden:
- Organisationsebene
- Ordnerebene
- Projektebene
- Ressourcenebene (Buckets, BigQuery-Datasets usw.)
- Rechnungskontoebene
IAM-Bindings, die auf Organisations- oder Ordnerebene angewendet werden, werden von allen untergeordneten Objekten geerbt.
Deklarative Binding-Definitionen und GitOps
Die Anleitung zeigt Ihnen, wie Sie IAM-Bindings in Ihrem Quellcode-Repository pflegen, sodass es zur Source of Truth für Zustand und Historie wird, ein Konzept, das als GitOps bekannt ist, und wie Sie Änderungen mit StackQL über CI/CD-Workflows anwenden.
Dieses Beispiel zeigt Ihnen, wie Sie viele Mitglieder oder Principals auf viele Rollen aus einem in der Versionsverwaltung gespeicherten json-Manifest abbilden.
Die GCP-Konsole sowie die Werkzeuge
terraformundgclouderlauben es nur, in jeder Operation ein Mitglied auf eine oder mehrere Rollen abzubilden
Funktionsweise
Das Framework, durch das wir Sie führen, stellt IAM-Bindings über alle Knoten Ihrer GCP-Hierarchie bereit und erstellt außerdem benutzerdefinierte Rollen (mit kuratierten Berechtigungen) - im Gegensatz zu vordefinierten Rollen wie zum Beispiel roles/bigquery.dataEditor.
Projektverzeichnisstruktur
Für dieses Bindings-Deployment-Framework wird der folgende Verzeichnisbaum verwendet:
├── iql
│ ├── iam.iql
│ ├── ...
├── data
│ ├── iam.jsonnet
│ ├── iam
│ │ ├── bindings.json
│ │ ├── custom_roles.json
Datei iql/iam.iql
Dies ist die Hauptdatei, die von stackql ausgeführt wird; mithilfe von data/iam.jsonnet rendert sie die aus den Daten in data/iam/bindings.json erzeugten, transpilierten Policy-Bindings und wendet sie an
diese Datei sollte nicht geändert werden müssen
Aufklappen, um die Datei iql/iam.iql anzuzeigen
/*
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
Diese Datei wird von iql/iam.iql vorverarbeitet, wobei die Daten aus data/iam/bindings.json bezogen werden
diese Datei sollte nicht geändert werden müssen
Aufklappen, um die Datei data/iam.jsonnet anzuzeigen
// 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
diese Datei muss geändert werden, um Policy-Bindings zu ändern
Dies ist die zentrale Quelle der Policy- und Binding-Daten; die Datenstruktur bildet einen oder mehrere Principals (Gruppen, Dienstkonten usw.) auf eine oder mehrere Rollen (vordefiniert oder benutzerdefiniert) auf einer bestimmten Ebene (parent) ab, wie unten gezeigt:
"<< level >>": [
{
"members": [
"<< principal >>",
...
],
"roles": [
"<< role >>",
...
]
},
zum Beispiel:
"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"
]
}
],
Deployment
Dieser Prozess wird bewusst nicht über eine CI/CD-Pipeline ausgeführt, da die Änderungen selten sind und wir einem Dienstkonto nicht die für dieses Deployment erforderlichen Berechtigungen geben möchten. Er ist von einem privilegierten Administrator auszuführen, der Mitglied der Google-Gruppe gcp-org-administrators ist.
Der Benutzer muss bei GCP authentifiziert sein, entweder über ein Dienstkonto oder über interaktive Authentifizierung; weitere Informationen zur Authentifizierung von StackQL bei Google finden Sie unter GCP-Authentifizierung.
Führen Sie den folgenden Befehl aus, um einen Dry Run durchzuführen:
stackql exec -i ./iql/iam.iql \
--iqldata ./data/iam.jsonnet \
--outfile iam-TEMPLATED.iql \
--dryrun --output text --hideheaders
Die resultierenden Daten in dieser Datei können als einzelne Anweisungen in stackql shell oder als Batch ausgeführt werden, indem Sie Folgendes ausführen:
stackql exec -i iam-TEMPLATED.iql \
Alternativ können Sie die von diesem Skript erzeugten Policies (JSON-Payloads) mit dem Befehl gcloud iam ausführen.