Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions gcp-integration-setup/docs/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,30 @@ Strict allowlist of `*.get` / `*.list` only.
| `artifactregistry.repositories.get/list` | Artifact Registry repo metadata (no image content). |
| `dns.managedZones.get/list` + `dns.resourceRecordSets.list` | Cloud DNS zone + record discovery. |
| `apigateway.gateways.get/list` + `apigateway.apis.get/list` + `apigateway.apiconfigs.get/list` | API Gateway topology. |
| `storage.buckets.get/list` + `storage.buckets.getIamPolicy` | Cloud Storage bucket settings + bucket-level IAM. No `storage.objects.*`. |
| `secretmanager.secrets.get/list` | Secret Manager: secret name, labels, replication policy, rotation config. No `secretmanager.versions.access` (payloads). |
| `bigquery.datasets.get/list` + `bigquery.tables.get/list` + `bigquery.routines.get/list` | BigQuery dataset/table/routine schema + IAM. No `bigquery.tables.getData` (rows) and no `bigquery.jobs.create` (no query execution / billing). |
| `cloudbuild.buildTriggers.get/list` | Cloud Build trigger config (repo binding, file filter, substitutions). No build logs or artifacts. |
| `batch.jobs.get/list` | Cloud Batch job spec. No task logs or output artifacts. |
| `workflows.workflows.get/list` | Cloud Workflows: workflow definitions only. **Not** `workflows.executions.*` or `workflows.stepEntries.*` — execution arguments and step inputs/outputs are runtime data. |
| `datastore.databases.list` + `datastore.databases.getMetadata` | Firestore database list + metadata. **Not** `datastore.entities.*` — document contents are runtime data. (Firestore in Native and Datastore modes share the `datastore.*` IAM family.) |
| `aiplatform.endpoints.get/list` | Vertex AI endpoint deployment config. No `aiplatform.endpoints.predict` (inference) and no model/dataset/featurestore reads. |
| `securitycenter.sources.get/list` | Security Command Center source config (which detection sources are wired up). **Not** `securitycenter.findings.*` or `securitycenter.assets.*` — finding contents are runtime data. Org-scope only; harmless no-op at project scope. |

## What Nullify cannot do

| Capability | Granted? | Why not |
| --- | --- | --- |
| Read object data from Cloud Storage | No | `roles/storage.objectViewer` is intentionally **not** granted. We only see bucket metadata. |
| Read secret payloads from Secret Manager | No | `roles/secretmanager.secretAccessor` is intentionally **not** granted. We only see secret names and metadata. |
| Read BigQuery table rows | No | `roles/bigquery.dataViewer` is intentionally **not** granted. We only see dataset metadata. |
| Read object data from Cloud Storage | No | `roles/storage.objectViewer` is intentionally **not** granted. We only see bucket settings + bucket IAM. |
| Read secret payloads from Secret Manager | No | `roles/secretmanager.secretAccessor` is intentionally **not** granted. We only see secret names, labels, replication policy. |
| Read BigQuery table rows | No | `roles/bigquery.dataViewer` is intentionally **not** granted. We only see dataset/table schema + IAM. |
| Run BigQuery queries | No | `bigquery.jobs.create` is **not** granted. No query execution and no billable jobs. |
| Read Workflow execution payloads | No | `workflows.executions.*` and `workflows.stepEntries.*` are **not** granted. We only see workflow definitions, never the inputs/outputs of an execution. The predefined `roles/workflows.viewer` is **not** used because it would expose execution payloads. |
| Read Firestore document contents | No | `datastore.entities.*` is **not** granted. We only see the database list. The predefined `roles/datastore.viewer` is **not** used because it grants document reads. |
| Run Vertex AI inference | No | `aiplatform.endpoints.predict` and `computeTokens` are **not** granted. We see endpoint config only — not models, datasets, or featurestores. The broader `roles/aiplatform.viewer` is **not** used. |
| Read SCC findings | No | `securitycenter.findings.*` and `securitycenter.assets.*` are **not** granted. We only see which detection sources are configured. |
| Read Cloud Build logs or artifacts | No | Trigger config only. No build logs, artifacts, or source contents. |
| Read Cloud Batch task logs | No | Job spec only. No task logs or output artifacts. |
| Modify your environment | No | Every role above is read-only. There are no write/admin roles. |
| Run code or workloads | No | No `roles/run.invoker`, `roles/cloudfunctions.invoker` etc. |

Expand Down
2 changes: 1 addition & 1 deletion gcp-integration-setup/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ CUSTOM_ROLE_TITLE="Nullify Cloud Connector (read-only)"
CUSTOM_ROLE_DESCRIPTION="Read-only access to security-relevant config Nullify needs that is not covered by predefined viewer roles."

# shellcheck disable=SC2089
CUSTOM_ROLE_PERMISSIONS="compute.securityPolicies.get,compute.securityPolicies.list,accesscontextmanager.accessPolicies.get,accesscontextmanager.accessPolicies.list,accesscontextmanager.servicePerimeters.get,accesscontextmanager.servicePerimeters.list,orgpolicy.policies.list,orgpolicy.policy.get,alloydb.clusters.get,alloydb.clusters.list,alloydb.instances.get,alloydb.instances.list,file.instances.get,file.instances.list,redis.instances.get,redis.instances.list,memcache.instances.get,memcache.instances.list,artifactregistry.repositories.get,artifactregistry.repositories.list,dns.managedZones.get,dns.managedZones.list,dns.resourceRecordSets.list,apigateway.gateways.get,apigateway.gateways.list,apigateway.apis.get,apigateway.apis.list,apigateway.apiconfigs.get,apigateway.apiconfigs.list"
CUSTOM_ROLE_PERMISSIONS="compute.securityPolicies.get,compute.securityPolicies.list,accesscontextmanager.accessPolicies.get,accesscontextmanager.accessPolicies.list,accesscontextmanager.servicePerimeters.get,accesscontextmanager.servicePerimeters.list,orgpolicy.policies.list,orgpolicy.policy.get,alloydb.clusters.get,alloydb.clusters.list,alloydb.instances.get,alloydb.instances.list,file.instances.get,file.instances.list,redis.instances.get,redis.instances.list,memcache.instances.get,memcache.instances.list,artifactregistry.repositories.get,artifactregistry.repositories.list,dns.managedZones.get,dns.managedZones.list,dns.resourceRecordSets.list,apigateway.gateways.get,apigateway.gateways.list,apigateway.apis.get,apigateway.apis.list,apigateway.apiconfigs.get,apigateway.apiconfigs.list,storage.buckets.get,storage.buckets.list,storage.buckets.getIamPolicy,secretmanager.secrets.get,secretmanager.secrets.list,bigquery.datasets.get,bigquery.datasets.list,bigquery.tables.get,bigquery.tables.list,bigquery.routines.get,bigquery.routines.list,cloudbuild.buildTriggers.get,cloudbuild.buildTriggers.list,batch.jobs.get,batch.jobs.list,workflows.workflows.get,workflows.workflows.list,datastore.databases.getMetadata,datastore.databases.list,aiplatform.endpoints.get,aiplatform.endpoints.list,securitycenter.sources.get,securitycenter.sources.list"

echo "==> Creating organisation custom role ${CUSTOM_ROLE_ID}"
if gcloud iam roles describe "${CUSTOM_ROLE_ID}" --organization="${NULLIFY_ORG_ID}" >/dev/null 2>&1; then
Expand Down
12 changes: 8 additions & 4 deletions gcp-integration-setup/terraform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ secrets.
Nullify's OIDC issuer URL, with an `attribute_condition` pinned to your
specific Nullify tenant id.
- A custom role with read-only permissions on the long-tail services that
don't have a predefined viewer role (Cloud Armor, VPC Service Controls,
AlloyDB, Filestore, Memorystore, Cloud DNS, API Gateway, Artifact
Registry). Defined at the org for `scope = "organization" | "folder"`,
at the project for `scope = "projects"`.
don't have a suitable predefined viewer role (Cloud Armor, VPC Service
Controls, AlloyDB, Filestore, Memorystore, Cloud DNS, API Gateway,
Artifact Registry, Cloud Storage, Secret Manager, BigQuery, Cloud Build,
Cloud Batch, Cloud Workflows, Firestore, Vertex AI, Security Command
Center). Defined at the org for `scope = "organization" | "folder"`,
at the project for `scope = "projects"`. Strict allowlist of `*.get` /
`*.list` only — no data-plane reads (no object/secret/row/document
contents, no execution payloads, no inference, no findings).
- IAM bindings granting the Nullify service account a curated set of
predefined viewer roles plus the custom role above. Bound at organisation
scope by default; folder and per-project scopes are also supported.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
# and network-topology metadata. There are NO data-plane permissions:
# - storage: bucket metadata only, never object data
# - secret manager: secret metadata only, never secret payloads
# - bigquery: dataset metadata only, never table rows
# - bigquery: schema/IAM only, never table rows
# - workflows: workflow definitions only, never execution inputs/outputs
# - firestore: database list only, never document contents
# - vertex ai: endpoint config only, never inference inputs/outputs
# - security command center: source config only, never finding contents
#
# See ../../docs/permissions.md for the full permission list + rationale.

Expand Down Expand Up @@ -166,6 +170,62 @@ locals {
"apigateway.apis.list",
"apigateway.apiconfigs.get",
"apigateway.apiconfigs.list",

# Cloud Storage bucket settings + bucket-level IAM (no object data —
# storage.objects.* is intentionally not granted).
"storage.buckets.get",
"storage.buckets.list",
"storage.buckets.getIamPolicy",

# Secret Manager: secret name, labels, replication policy, rotation
# config (no secretmanager.versions.access — payloads are never read).
"secretmanager.secrets.get",
"secretmanager.secrets.list",

# BigQuery dataset/table/routine schema + IAM. No bigquery.tables.getData
# (row data) and no bigquery.jobs.create (no query execution / billing).
"bigquery.datasets.get",
"bigquery.datasets.list",
"bigquery.tables.get",
"bigquery.tables.list",
"bigquery.routines.get",
"bigquery.routines.list",

# Cloud Build trigger config (repo binding, file filter, substitutions).
# No build logs, artifacts, or source contents.
"cloudbuild.buildTriggers.get",
"cloudbuild.buildTriggers.list",

# Cloud Batch job spec. No task logs or output artifacts.
"batch.jobs.get",
"batch.jobs.list",

# Cloud Workflows: workflow definitions only. workflows.executions.* and
# workflows.stepEntries.* are intentionally NOT granted — execution
# arguments and step inputs/outputs are runtime data.
"workflows.workflows.get",
"workflows.workflows.list",

# Firestore: database list + metadata. datastore.entities.* is
# intentionally NOT granted — document contents are runtime data.
# (Note: GCP uses the datastore.* IAM family for Firestore in both
# Native and Datastore modes; firestore.* may be added later as GCP
# migrates the IAM surface.)
"datastore.databases.getMetadata",
"datastore.databases.list",

# Vertex AI: endpoint deployment config only. aiplatform.endpoints.predict
# / computeTokens and all dataset/featurestore/model perms are
# intentionally NOT granted.
"aiplatform.endpoints.get",
"aiplatform.endpoints.list",

# Security Command Center: source config (which detection sources are
# wired up). securitycenter.findings.* and securitycenter.assets.* are
# intentionally NOT granted — finding contents are runtime data.
# Org-scope only — at project scope these calls return empty harmlessly.
"securitycenter.sources.get",
"securitycenter.sources.list",
]
}

Expand Down
Loading