Skip to content

Security Posture

asbrodova edited this page Aug 13, 2026 · 3 revisions

Project Security Posture

Module flag: security
Enabled by default: yes
Tool: gcp_project_security_audit
Mutation: no — the complete workflow is read-only


Overview

The security module turns a broad request such as:

"I need a project audit."

into one deterministic GCP security assessment. It collects live project configuration, evaluates versioned rules, and returns a score with findings grouped under Critical, High, Medium, and Low. Every report also includes prioritized recommendations and explicit coverage gaps.

The wording does not need to match an exact trigger. The MCP tool description intentionally covers requests such as:

  • "Audit my project."
  • "Is my GCP setup secure?"
  • "Review our cloud security."
  • "Find security risks in production."
  • "Check IAM and networking."
  • "Give me a security score."
  • "Are any services publicly exposed?"

Cost-only, reliability-only, and architecture-only audits continue to route to their dedicated modules.


How It Works

flowchart TB
    User(["Project audit request"]) --> MCP["gcp_project_security_audit"]
    MCP --> Engine["Bounded parallel collector"]

    subgraph Identity["Identity and hierarchy"]
        direction TB
        Org["Organization IAM + deny"] --> Folder["Folder IAM + deny"] --> Project["Project/resource IAM + deny"]
        Project --> SA["Service accounts + keys"]
        Project --> Secrets["Secret Manager"]
    end

    subgraph Network["Effective network exposure"]
        direction TB
        HFP["Hierarchical policies"] --> Effective["Effective firewall order"]
        GFP["Global network policies"] --> Effective
        RFP["Regional network policies"] --> Effective
        Classic["Classic VPC rules"] --> Effective
    end

    subgraph Kubernetes["GKE runtime evidence"]
        direction TB
        Direct["Direct Kubernetes API"] -. fallback .-> Gateway["Connect Gateway"]
        Direct --> Objects["Services · Ingresses · Gateways · HTTPRoutes"]
        Gateway --> Objects
        Objects --> KSA["Workloads · KSAs · GSA mappings"]
    end

    Engine --> Identity
    Engine --> Network
    Engine --> Kubernetes
    Engine --> Serverless["Cloud Run + Functions"]
    Engine --> Rec["Active Assist"]

    Identity & Network & Kubernetes & Serverless & Rec --> Facts["Normalized evidence + per-scope coverage"]
    Facts --> Rules["security-posture-v2 rules"]
    Rules --> Score["Weighted score + confidence gate"]
    Score --> Report["Critical · High · Medium · Low · Recommendations"]

    classDef entry fill:#ede9fe,stroke:#6d28d9,color:#2e1065;
    classDef collect fill:#e0f2fe,stroke:#0284c7,color:#082f49;
    classDef reason fill:#dcfce7,stroke:#16a34a,color:#052e16;
    class User,MCP entry;
    class Engine,Org,Folder,Project,SA,Secrets,HFP,GFP,RFP,Classic,Effective,Direct,Gateway,Objects,KSA,Serverless,Rec collect;
    class Facts,Rules,Score,Report reason;
Loading

The GCP adapter only collects normalized facts. The application-layer engine owns rules, severity, deduplication, coverage, scoring, and rendering. The LLM presents the returned evidence; it does not invent or score findings.


Audit Domains

Domain Examples of evaluated risk
IAM Project, folder, and organization allow-policy inheritance; deny-policy evidence; public principals; Owner/Editor grants; project-wide service-account impersonation; deleted principals
Service accounts Exposed or active user-managed keys, retained disabled keys, disabled accounts with live bindings
Secret Manager Public accessor bindings, overdue rotation, missing enabled versions, referenced secrets without a rotation schedule
Public services Public Run/Functions IAM; external GKE LoadBalancer Services, Ingresses, Gateways, and HTTPRoutes; plaintext listeners; exposed sensitive ports; backend KSA evidence
Firewall Effective classic, hierarchical, global-network, regional-network, and system policy order; internet-wide ingress; shadowed allows; sensitive ports; missing logging
Workload Identity Cluster/node configuration, KSA-to-workload use, direct KSA principals, namespace principal sets, annotated GSA impersonation, mapped GSA roles, cross-project GSA verification
Recommendations Active IAM recommendations from Google Active Assist, deduplicated with local evidence where possible

Public HTTP is not automatically Critical. Severity rises when public invocation is combined with a privileged runtime identity, insecure transport, unrestricted protocols, or other concrete evidence.


Score

The score is versioned as security-posture-v2.

Category Weight
IAM 25%
Service accounts 20%
Public services 20%
Firewall 15%
Secret Manager 10%
Workload Identity 10%

Severity penalties are 40 for Critical, 20 for High, 8 for Medium, and 3 for Low. Repeated instances of one rule use diminishing penalties so a large project is not punished linearly for the same root cause.

Global caps keep serious findings visible:

  • Any Critical finding caps the score at 49.
  • Any High finding caps it at 79.
  • Any Medium finding caps it at 89.
  • Low-only findings cap it at 99.

Coverage is independent of posture. At 90% or higher the score is final; between 60% and 89% it is provisional. Below 60%, or when project IAM could not be assessed, the score is unavailable instead of falsely reassuring.

Failed categories are excluded from the weighted-score denominator and carry their collector status beside the category score. Partial categories contribute in proportion to their completed scope units; one inaccessible cluster or ancestor no longer degrades the category by an arbitrary fixed amount. Truncated scans are marked partial rather than clean.

Active Assist IAM recommendations share the per-recommender quota gate used by Aura and cost reasoning. Quota exhaustion marks recommendation coverage partial with its window and RFC3339 retry time; deterministic security rules and every other collector continue. A degraded five-minute report cache expires at the retry deadline when that is sooner.


Example Output

Security posture: 63/100
Coverage: 93% — final score
Critical: 1 · High: 3 · Medium: 4 · Low: 2

Critical

[IAM-001-A84F21] Public principal has an IAM role
Resource: //cloudresourcemanager.googleapis.com/projects/example-prod
Evidence: allUsers has roles/owner; unconditional binding
Recommendation: Remove the public binding or replace it with the narrowest intended principal and role.

High
...

Coverage gaps
- iam: partial — iam_deny for organizations/123: permission denied
- workload_identity: partial — kubernetes_access for projects/example-prod/locations/us/clusters/private-a: RBAC denied

Every severity heading is present even when it has no findings.


Accepted-Risk Suppressions

Known intentional exposure can be suppressed in ~/.aura-tracker.yaml without deleting its audit trail:

security_audit:
  kubernetes_access: auto       # auto, direct, connect_gateway, or disabled
  fleet_project_id: platform-fleet
  cluster_concurrency: 4
  per_cluster_timeout_seconds: 20
  max_resources_per_kind: 2000
  suppressions:
    - rule_id: PUB-001
      resource: "//run.googleapis.com/projects/example-prod/locations/*/services/public-api"
      reason: "Approved public API behind the external application load balancer"
      owner: "platform-security@example.com"
      expires_at: "2026-12-01T00:00:00Z"

rule_id, resource, reason, and expires_at are required. Resources support * and ? wildcards. Active matches are excluded from finding counts and scoring but stay visible under Suppressed accepted risks with their reason, owner, and expiry. Expired suppressions stop matching automatically; malformed or permanent suppressions stop server startup.

Kubernetes collection settings:

Setting Default Meaning
kubernetes_access auto Try the direct control-plane endpoint, then Connect Gateway; direct, connect_gateway, and disabled force one behavior
fleet_project_id audited project Fleet host used to locate a matching GKE membership
cluster_concurrency 4 Maximum clusters enriched concurrently in each collector
per_cluster_timeout_seconds 20 Deadline for access and Kubernetes reads in one cluster
max_resources_per_kind 2000 Safety cap; reaching it marks that scope truncated and the category partial

Tool Parameters

Parameter Required Default Description
project_id no default environment Environment alias or configured project ID to audit
refresh no false Bypass the five-minute in-process report cache after remediation

The structured result contains the complete report model and the text fallback contains the formatted severity report.


Required APIs and IAM

Recommended setup for the default security module:

PROJECT_ID=my-project MODULES=security bash scripts/setup-iam.sh

The module-aware setup enables the Cloud Asset, IAM, Secret Manager, Compute, Cloud Functions, Recommender, GKE Hub, and Connect Gateway APIs and grants the read-only project/fleet roles below. Add SECURITY_AUDIT_ENABLED=true when the fleet host differs or when organization-level IAM/deny readers must also be reconciled.

  • roles/cloudasset.viewer
  • roles/iam.serviceAccountViewer
  • roles/secretmanager.viewer
  • roles/compute.viewer
  • roles/cloudfunctions.viewer
  • roles/recommender.iamViewer
  • roles/gkehub.viewer
  • roles/gkehub.gatewayReader
  • roles/serviceusage.serviceUsageConsumer

Core setup already grants roles/run.viewer and roles/container.viewer. To make ancestor collection complete, an organization administrator can also run setup with SECURITY_AUDIT_ORGANIZATION_ID=123456789; this adds read-only roles/iam.securityReviewer, roles/iam.denyReviewer, and roles/browser at that organization.

Kubernetes resources require Kubernetes RBAC in addition to Google Cloud IAM. Replace AURA_SECURITY_PRINCIPAL in deploy/security-audit-rbac.yaml.tmpl and apply it to every directly accessed cluster. For Connect Gateway, also generate the gateway impersonation policy for that principal:

gcloud container fleet memberships generate-gateway-rbac \
  --membership=MEMBERSHIP \
  --role=clusterrole/aura-security-auditor \
  --users=aura-tracker-mcp@my-project.iam.gserviceaccount.com \
  --project=FLEET_PROJECT \
  --kubeconfig=KUBECONFIG --context=CONTEXT --apply

roles/secretmanager.viewer can list secret and version metadata but cannot access secret payloads. The audit never calls Secret Manager's payload-access API and never receives service-account private key data.

If Recommender is disabled with RECOMMENDER_ENABLED=false, deterministic recommendations still appear, while the report marks Active Assist enrichment as skipped.


Safety and Limitations

  • Organization/folder allow and deny policies are collected, but each ancestor requires explicit read permission. Missing scopes appear as granular coverage units.
  • IAM Conditions are preserved as evidence; request-dependent CEL expressions are not simulated. Deny rules are shown alongside matching allows, without pretending to expand every role into permissions offline.
  • Effective firewall endpoints supply policy ordering and preserve source address groups, FQDNs, networks, region codes, threat-intelligence selectors, network context/type, service accounts, tags, and secure-tag state. A rule with a source selector is not assumed world-wide merely because it lacks CIDRs. The auditor only treats an allow as fully shadowed when an earlier world-wide deny demonstrably covers its effective targets, protocols, and ports; ineffective secure tags do not create phantom coverage.
  • GKE LoadBalancer, externalIP, Ingress, Gateway, and HTTPRoute exposure is assessed. A bare NodePort is not labeled internet-facing without independent public node/routing evidence.
  • Direct and cross-project GSA mappings are checked when the auditor can read the target service account and target project IAM policies; otherwise the exact GSA scope is partial.
  • Private clusters require Connect Gateway or network reachability plus the read-only Kubernetes RBAC described above.
  • Active Assist IAM recommendations are supplemental and non-fatal.
  • Security Command Center findings are not imported in this release.
  • The tool recommends remediation but never applies it.

Treat the score as prioritized posture evidence, not a compliance certification or penetration test.

Clone this wiki locally