Skip to main content

import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';

Required Permissions

Most onboarding problems are missing permissions, not bad configuration. This page lists exactly what Offload Security needs, per cloud. Access is read-only everywhere except the one-time Cloud Events setup.

Think of it as three buckets — you don't need all of them for every feature:

BucketWhat it isWhere it's granted
A. Read-only scanningAsset Inventory, Cloud/CSPM, Kubernetes & registry discoveryAn IAM role / service account / service principal on the cloud
B. Kubernetes in-cluster RBACReading workloads/RBAC/network policies inside a clusterA Kubernetes ClusterRole bound inside each cluster
C. Cloud Events setupStreaming audit/activity events to Offload SecurityOne-time event source → webhook wiring

Bucket A — Read-only scanning

Pick your cloud. These are the exact roles the connect wizard provisions.

Offload Security assumes a cross-account IAM role via STS (with an External ID) — no long-lived keys required.

Managed policies: SecurityAudit + ReadOnlyAccess Plus an inline policy for native-findings ingestion: securityhub, guardduty, inspector2, access-analyzer, config, cloudtrail.

CloudFormation

Resources:
CSPMSecurityAuditRole:
Type: AWS::IAM::Role
Properties:
RoleName: CSPMSecurityAuditRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal: { AWS: !Sub 'arn:aws:iam::${TrustedAccountId}:root' } # Offload Security's account
Action: sts:AssumeRole
Condition: { StringEquals: { sts:ExternalId: !Ref ExternalId } }
ManagedPolicyArns:
- arn:aws:iam::aws:policy/SecurityAudit
- arn:aws:iam::aws:policy/ReadOnlyAccess
Policies:
- PolicyName: CSPMAdditionalPermissions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: [securityhub:*, guardduty:*, inspector2:*, access-analyzer:*, config:*, cloudtrail:*]
Resource: '*'

Terraform

resource "aws_iam_role" "cspm_security_audit" {
name = "CSPMSecurityAuditRole"
assume_role_policy = data.aws_iam_policy_document.assume_role.json # trusts Offload's account + external_id
}
resource "aws_iam_role_policy_attachment" "security_audit" {
role = aws_iam_role.cspm_security_audit.name
policy_arn = "arn:aws:iam::aws:policy/SecurityAudit"
}
resource "aws_iam_role_policy_attachment" "read_only" {
role = aws_iam_role.cspm_security_audit.name
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}

You then paste the Role ARN and External ID into the connect wizard (access keys are also supported, but the cross-account role is preferred).

Offload Security uses a service account (key or Workload Identity Federation).

Enable the APIs:

gcloud services enable \
cloudasset.googleapis.com cloudresourcemanager.googleapis.com \
compute.googleapis.com iam.googleapis.com storage.googleapis.com \
logging.googleapis.com monitoring.googleapis.com securitycenter.googleapis.com \
container.googleapis.com artifactregistry.googleapis.com pubsub.googleapis.com

Roles (the wizard's project template + the org-only additions):

RoleFor
roles/cloudasset.viewerasset inventory (searchAllResources)
roles/iam.securityReviewerIAM posture
roles/securitycenter.findingsViewerSCC findings (org)
roles/compute.viewer, roles/storage.objectViewercompute / storage posture
roles/logging.viewer, roles/monitoring.viewerlogs / metrics
roles/container.viewerGKE discovery
roles/artifactregistry.readerregistry discovery
roles/resourcemanager.organizationViewer + folderViewerenumerate all projects (org only)

Grant at the org node (covers all current + future projects):

for R in roles/cloudasset.viewer roles/resourcemanager.organizationViewer \
roles/resourcemanager.folderViewer roles/iam.securityReviewer \
roles/securitycenter.findingsViewer roles/compute.viewer \
roles/storage.objectViewer roles/logging.viewer roles/monitoring.viewer \
roles/container.viewer roles/artifactregistry.reader ; do
gcloud organizations add-iam-policy-binding "$ORG_ID" \
--member="serviceAccount:$SA_EMAIL" --role="$R" --condition=None
done

Terraform: google_organization_iam_member with for_each over the same list.

Offload Security uses a service principal (app registration) with roles at subscription scope.

Roles: Reader, Security Reader, Key Vault Reader, Network Contributor, Storage Blob Data Reader.

Azure CLI

# Create the SP with Reader, then add the security roles
SP=$(az ad sp create-for-rbac --name "offloadsecurity-cspm" \
--role "Reader" --scopes "/subscriptions/$SUBSCRIPTION_ID" -o json)
CLIENT_ID=$(echo $SP | jq -r '.appId')

for ROLE in "Security Reader" "Key Vault Reader" "Network Contributor" "Storage Blob Data Reader" ; do
az role assignment create --assignee "$CLIENT_ID" --role "$ROLE" \
--scope "/subscriptions/$SUBSCRIPTION_ID"
done
# Paste appId (client_id), password (client_secret), tenant into the wizard.

Terraform: azuread_application + azuread_service_principal + azurerm_role_assignment (for_each over the five roles) at subscription scope.

Scope = grant once

Grant at the AWS Organization / GCP org node / Azure management group to cover every account/project/subscription (current and future) with one binding. Everything above also works at single account/project/subscription scope.


Bucket B — Kubernetes in-cluster RBAC

Cluster discovery (EKS / GKE / AKS) is already covered by the read roles in Bucket A. But reading workloads, RBAC, and network policies inside a cluster requires Kubernetes RBAC — apply this ClusterRole in each cluster and bind it to the identity Offload Security uses (the kubeconfig / service-account token you provide at onboarding):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: offloadsecurity-scanner
rules:
- apiGroups: [""]
resources: ["pods","nodes","services","namespaces","endpoints","persistentvolumes","persistentvolumeclaims","configmaps"]
verbs: ["get","list"]
- apiGroups: ["apps"]
resources: ["deployments","daemonsets","statefulsets","replicasets"]
verbs: ["get","list"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles","rolebindings","clusterroles","clusterrolebindings"]
verbs: ["get","list"]
- apiGroups: ["networking.k8s.io"]
resources: ["networkpolicies","ingresses"]
verbs: ["get","list"]
- apiGroups: ["batch"]
resources: ["jobs","cronjobs"]
verbs: ["get","list"]
- apiGroups: ["policy"]
resources: ["podsecuritypolicies","poddisruptionbudgets"]
verbs: ["get","list"]

Clusters are onboarded with a read-only kubeconfig or service-account token (base64-encoded) — see Kubernetes Security.


Container registries

Registry discovery rides on Bucket A; pulling images to scan needs pull access:

RegistryPull permission
AWS ECR (*.dkr.ecr.*.amazonaws.com)ecr:GetAuthorizationToken + read — already in ReadOnlyAccess (or attach AmazonEC2ContainerRegistryReadOnly)
GCP Artifact Registry / GCRroles/artifactregistry.reader (+ roles/storage.objectViewer for legacy gcr.io)
Azure ACR (*.azurecr.io)AcrPull — add this role to the service principal (it is not in the base Azure role set above)

See Container Security.


Bucket C — Cloud Events (audit/activity streaming)

Cloud Events is push-based: your cloud sends audit/activity events to an Offload Security webhook. This is a one-time setup, and the scanning role needs no extra permission for ongoing ingestion.

CloudEvent source → webhook
AWSCloudTrail → EventBridge rule → API destination → POST /api/cloud-events/webhook/aws
GCPCloud Audit Logs → Pub/Sub log sink → push subscription → POST /api/cloud-events/webhook/gcp
AzureActivity Log → Event Grid system topic → subscription → POST /api/cloud-events/webhook/azure

The setup permissions belong to whoever creates the event source (an admin): create an EventBridge rule (AWS), a Logging sink + Pub/Sub topic/subscription (GCP — logging.configWriter + pubsub.admin), or an Event Grid subscription (Azure). The connect UI shows you the exact per-provider webhook URL to target.


Quick reference

CloudBucket A (scanning)Registry pullCloud Events
AWSCross-account role: SecurityAudit + ReadOnlyAccess (+ securityhub/guardduty/inspector2/access-analyzer/config/cloudtrail)ECR (in ReadOnlyAccess)EventBridge → webhook
GCPcloudasset.viewer, iam.securityReviewer, securitycenter.findingsViewer, compute/storage/logging/monitoring.viewer, container.viewer, artifactregistry.reader (+ organizationViewer at org)artifactregistry.readerPub/Sub sink → webhook
AzureSP: Reader, Security Reader, Key Vault Reader, Network Contributor, Storage Blob Data ReaderAcrPull (add)Event Grid → webhook

Notes

  • Least privilege. Every role is read-only except the one-time Cloud Events setup. On AWS the cross-account role uses an External ID; on GCP prefer Workload Identity Federation (keyless); on Azure the SP secret should be rotated (the wizard defaults it to 1 year).
  • Authentication into the wizard. AWS = role ARN + external ID (or access keys); GCP = service-account JSON (or WIF); Azure = client id + secret + tenant.
  • Scope. Grant at account/project/subscription scope, or at the org/management-group for one-and-done coverage of all children.