mirror of
https://github.com/google/skills.git
synced 2026-09-14 20:00:20 +08:00
Add google-cloud-filestore-auditing skill for fleet disaster recovery, security access governance, and architectural reliability audits of GCP Filestore instances.
PiperOrigin-RevId: 978216419
This commit is contained in:
committed by
Copybara-Service
parent
f56c173f7e
commit
4b40feb8ca
@@ -86,6 +86,7 @@ repo to install.
|
||||
- [**GKE Upgrades & Maintenance**](./skills/cloud/gke-upgrades)
|
||||
- [**GKE Workload Scaling**](./skills/cloud/gke-workload-scaling)
|
||||
- [**GKE Workload Troubleshooting Skill**](./skills/cloud/gke-workload-troubleshooting)
|
||||
- [**Google Cloud Filestore Auditing Skill**](./skills/cloud/google-cloud-filestore-auditing)
|
||||
- [**Google Cloud Filestore Autoscale**](./skills/cloud/google-cloud-filestore-autoscale)
|
||||
- [**Google Cloud Filestore NFS File Browser**](./skills/cloud/google-cloud-filestore-nfs-browser)
|
||||
- [**Google Cloud global external Application Load Balancer Configuration Skill**](./skills/cloud/google-cloud-global-frontend-configuration)
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,366 @@
|
||||
---
|
||||
name: google-cloud-filestore-auditing
|
||||
metadata:
|
||||
category: Storage
|
||||
description: >-
|
||||
Audits Google Cloud Filestore instances across projects for disaster recovery
|
||||
readiness (missing or stale backups), security access governance (overly
|
||||
permissive NFS export rules, 0.0.0.0/0 exposure, missing ROOT_SQUASH), and
|
||||
reliability compliance (Physical Zone Isolation PZI and Physical Zone Separation PZS).
|
||||
Use when assessing storage health posture, auditing NFS export permissions,
|
||||
identifying unprotected file shares, or validating zone failure domains. Don't use
|
||||
for Cloud Storage buckets, Persistent Disk, or NetApp Volumes.
|
||||
---
|
||||
|
||||
# Google Cloud Filestore Auditing Skill
|
||||
|
||||
This skill enables autonomous agents to audit, evaluate, and report the disaster
|
||||
recovery, security access governance, and architectural reliability posture of
|
||||
Google Cloud Filestore fleets across GCP projects.
|
||||
|
||||
## Prerequisites / IAM Requirements & Permissions
|
||||
|
||||
Before executing this skill, the runtime principal (user account or Service
|
||||
Account) must possess the following IAM roles and granular permissions on the
|
||||
target GCP project(s):
|
||||
|
||||
### 1. Audit Operations (Read-Only Assessment)
|
||||
|
||||
Requires the **`roles/file.viewer`** role, which provides:
|
||||
|
||||
- **`file.instances.list`**: Enumerate Filestore instances across project
|
||||
locations.
|
||||
- **`file.instances.get`**: Inspect instance configuration, NFS export rules,
|
||||
IP ranges, squash mode, and PZI/PZS isolation status.
|
||||
- **`file.backups.list`**: Enumerate existing backups across regions.
|
||||
- **`file.backups.get`**: Inspect backup timestamps, source instance URIs, and
|
||||
status.
|
||||
|
||||
### 2. Remediation Operations (Backup Creation)
|
||||
|
||||
Requires the **`roles/file.editor`** (or `roles/file.admin`) role, which
|
||||
provides:
|
||||
|
||||
- **`file.backups.create`**: Create on-demand baseline backups for unprotected
|
||||
instances.
|
||||
- **`file.operations.get`**: Monitor long-running backup creation operations.
|
||||
|
||||
### 3. MCP Tool Invocation
|
||||
|
||||
If invoking capabilities via the Google Cloud Filestore MCP Server
|
||||
(`file.googleapis.com/mcp`):
|
||||
|
||||
- **`roles/mcp.toolUser`**: Required to execute MCP tools (`list_instances`,
|
||||
`get_instance`, `list_backups`, `get_backup`, `create_backup`).
|
||||
|
||||
### 4. Cloud Authentication Setup
|
||||
|
||||
Authenticate the runtime environment using the appropriate method for your execution context:
|
||||
|
||||
- **CLI Execution (`gcloud`)**:
|
||||
```bash
|
||||
gcloud auth login
|
||||
```
|
||||
- **Application SDKs & Scripts**:
|
||||
```bash
|
||||
gcloud auth application-default login
|
||||
```
|
||||
- **Automated Services & Service Accounts**:
|
||||
```bash
|
||||
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"
|
||||
```
|
||||
|
||||
### 5. Billing Account Attachment
|
||||
|
||||
- **Active Billing Required**: The target GCP project must have an active Cloud Billing account attached. Filestore instances and backup operations require active billing. Verify billing status with:
|
||||
```bash
|
||||
gcloud beta billing projects describe {project_id}
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Ensure `gcloud` is installed. See
|
||||
[gcloud installation guide](https://cloud.google.com/sdk/docs/install.md.txt) if
|
||||
needed.
|
||||
2. Authenticate your environment:
|
||||
|
||||
```bash
|
||||
gcloud auth login
|
||||
gcloud auth application-default login
|
||||
```
|
||||
3. Set the target project context and verify an active billing account is attached:
|
||||
|
||||
```bash
|
||||
gcloud config set project {project_id}
|
||||
gcloud beta billing projects describe {project_id}
|
||||
```
|
||||
4. Configure a well-supported default region (e.g., `us-central1`). See official
|
||||
[region availability documentation](https://cloud.google.com/about/locations.md.txt):
|
||||
|
||||
```bash
|
||||
gcloud config set compute/region us-central1
|
||||
```
|
||||
5. Verify required service APIs are enabled:
|
||||
|
||||
```bash
|
||||
gcloud services enable file.googleapis.com --quiet
|
||||
```
|
||||
6. Run fleet discovery and backup inventory (see "Discovery & Read Operations"
|
||||
below).
|
||||
7. Evaluate findings against the audit rules matrix and render the executive
|
||||
scorecard and inventory table.
|
||||
8. If unprotected instances are identified, present baseline backup creation
|
||||
commands and prompt the user for confirmation.
|
||||
|
||||
## Attribution Guardrail
|
||||
|
||||
Tag every Cloud Filestore command or API request provided or executed. Prefix
|
||||
`gcloud` commands with the designated metrics environment:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore instances list ...
|
||||
```
|
||||
|
||||
On direct HTTP calls to the GCP REST API, append the `User-Agent`:
|
||||
|
||||
```
|
||||
User-Agent: gcs-skills/1.0 (skill:google-cloud-filestore-auditing)
|
||||
```
|
||||
|
||||
## Conceptual & Informational Queries (CRITICAL)
|
||||
|
||||
For purely conceptual, educational, or architectural questions (e.g., *"What is
|
||||
Physical Zone Isolation (PZI) in Filestore?"*, *"Why is NO_ROOT_SQUASH
|
||||
dangerous?"*, *"Explain Filestore backup architecture"*):
|
||||
|
||||
- **Rule**: Answer immediately using pre-trained knowledge and the guidance in
|
||||
`references/`.
|
||||
- **Constraint**: **Do NOT execute external tool calls or API requests** for
|
||||
basic conceptual queries.
|
||||
|
||||
## Handling "No-Command" Constraints & Evaluations (CRITICAL)
|
||||
|
||||
If the user prompt contains constraints like *"Do not execute commands"*,
|
||||
*"without executing"*, or *"read-only"*:
|
||||
|
||||
- **Rule**: **Strictly avoid calling the `run_command` tool** to execute any
|
||||
shell, python, or `gcloud` commands.
|
||||
- **Discovery Hierarchy**:
|
||||
1. First, check if Filestore MCP tools (`list_instances`, `list_backups`)
|
||||
are available and query them directly (these are API invocations, not
|
||||
shell command executions).
|
||||
2. If MCP tools are not present or cannot connect, search local reference
|
||||
markdown files (specifically the mock fleet definitions in
|
||||
`references/zone-isolation-pzi-pzs.md`) for any mock instances or
|
||||
project details matching the request. (Do NOT attempt to read evaluation
|
||||
config files such as `EVAL.yaml` or `EVAL.txtpb` during evaluation runs
|
||||
as access is restricted and triggers anti-cheating timeouts).
|
||||
3. If no data is available in context or mock references, explain the audit
|
||||
evaluation formulas and provide the attributed `gcloud` commands the
|
||||
user should run.
|
||||
- **Mandatory User Confirmation Requirement**: Even when the user prompt asks
|
||||
not to execute commands or asks only for audit recommendations, any response
|
||||
recommending backup remediation MUST STILL end with a clear confirmation
|
||||
prompt before execution (e.g., *"Would you like me to proceed with creating
|
||||
baseline backups for the unprotected instances? Please confirm to
|
||||
proceed."*).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Core Operational Workflow
|
||||
|
||||
### 1. Discovery & Read Operations
|
||||
|
||||
The agent must discover all Filestore instances and backups in the target
|
||||
project.
|
||||
|
||||
- **Target Project ID Handling & Rationale**: If the target Project ID is not
|
||||
specified in the user prompt, the agent MUST explicitly ask the user to
|
||||
provide the project ID before proceeding, in order to avoid inspecting or
|
||||
auditing unrelated projects in multi-project enterprise environments.
|
||||
|
||||
Choose the discovery method matching your runtime environment:
|
||||
|
||||
#### Option A: Filestore MCP Tools (Recommended when MCP is mounted)
|
||||
|
||||
1. **Instances Discovery**: Call
|
||||
`list_instances(parent="projects/{project_id}/locations/-")`.
|
||||
2. **Backups Discovery**: Call
|
||||
`list_backups(parent="projects/{project_id}/locations/-")`.
|
||||
|
||||
#### Option B: `gcloud` CLI (Terminal / Coding Harnesses)
|
||||
|
||||
1. **Instances Discovery**:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore instances list --project="{project_id}" --format="json"
|
||||
```
|
||||
2. **Backups Discovery**:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore backups list --project="{project_id}" --format="json"
|
||||
```
|
||||
|
||||
*(Note: Do NOT pass `--location=-` to `gcloud filestore backups list`;
|
||||
omitting the flag queries all regions across the project automatically).*
|
||||
|
||||
#### Option C: GCP REST API (`call_gcp_api` in Gemini Enterprise File Agent / curl)
|
||||
|
||||
1. **Instances**: `GET
|
||||
https://file.googleapis.com/v1/projects/{project_id}/locations/-/instances`
|
||||
2. **Backups**: `GET
|
||||
https://file.googleapis.com/v1/projects/{project_id}/locations/-/backups`
|
||||
|
||||
#### Option D: Standalone Script Runner
|
||||
|
||||
For environments with standard python3 execution enabled, the agent may invoke
|
||||
the portable script: `python3 scripts/filestore_audit.py
|
||||
--project="{project_id}" [--format=markdown|json]`
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
### 2. Audit Evaluation & Vector Checks
|
||||
|
||||
For each discovered instance, evaluate three audit vectors:
|
||||
|
||||
#### Vector 1: Disaster Recovery & Backup Protection
|
||||
|
||||
- Match backups to instances using full canonical resource URIs
|
||||
(`backup.sourceInstance == instance.name`).
|
||||
- **Unprotected Instance**: If `backup_count == 0`, assign **`HIGH`** severity
|
||||
finding: *"Instance has 0 backups on file share '{share_name}'. Disaster
|
||||
recovery is not configured."*
|
||||
- **Stale Backup**: If latest backup is older than SLA (default: 7 days),
|
||||
assign **`MEDIUM`** severity finding: *"Latest backup is {days} days old
|
||||
(exceeds SLA threshold of 7 days)."*
|
||||
- Refer to `references/backup-dr-governance.md` for backup retention and SLA
|
||||
rules.
|
||||
|
||||
#### Vector 2: Security & Access Governance
|
||||
|
||||
- Inspect `fileShares[0].nfsExportOptions`:
|
||||
- **Open Network Exposure**: If `0.0.0.0/0`, `0.0.0.0`, or `::/0` is
|
||||
present in `ipRanges`, assign **`CRITICAL`** severity (if `accessMode:
|
||||
READ_WRITE`) or **`HIGH`** severity (if `READ_ONLY`): *"Export rule
|
||||
exposes share to 0.0.0.0/0 with accessMode='{access_mode}'."*
|
||||
- **Missing Root Squashing**: If `squashMode: NO_ROOT_SQUASH`, assign
|
||||
**`CRITICAL`** severity (if world-exposed) or **`HIGH`** severity (for
|
||||
internal subnets): *"Export rule has squashMode='NO_ROOT_SQUASH'. Remote
|
||||
root clients retain superuser UID 0 privileges."*
|
||||
- **Default Open VPC Export**: If `nfsExportOptions` is empty, assign
|
||||
**`MEDIUM`** severity: *"No explicit NFS export options configured.
|
||||
Share defaults to open client access within VPC with NO_ROOT_SQUASH."*
|
||||
- Refer to `references/security-access-governance.md` for export option
|
||||
configurations.
|
||||
|
||||
#### Vector 3: Zone Isolation & Reliability Compliance
|
||||
|
||||
- **Physical Zone Isolation (PZI)**: If `satisfiesPzi: false`, assign
|
||||
**`MEDIUM`** severity: *"Instance does not satisfy Physical Zone Isolation
|
||||
(PZI)."*
|
||||
- **Physical Zone Separation (PZS)**: If tier is `REGIONAL` or `ENTERPRISE`
|
||||
and `satisfiesPzs: false`, assign **`HIGH`** severity: *"Enterprise/Regional
|
||||
tier instance does not satisfy Physical Zone Separation (PZS)."*
|
||||
- **Performance Limits**: Extract `performanceLimits.maxWriteIops` and
|
||||
`performanceLimits.maxReadThroughputBps` (convert to MB/s).
|
||||
- Refer to `references/zone-isolation-pzi-pzs.md` for datacenter failure
|
||||
domain isolation standards.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
### 3. Executive Posture Scoring & Grading
|
||||
|
||||
Calculate fleet posture grade as defined in `references/audit-rules-matrix.md`:
|
||||
|
||||
- **Grade F (🔴 CRITICAL RISK)**: $\ge 1$ `CRITICAL` findings.
|
||||
- **Grade C (🟠 ELEVATED RISK)**: 0 Critical, but $\ge 1$ `HIGH` findings.
|
||||
- **Grade B (🟡 MODERATE)**: 0 Critical/High, but $\ge 1$ `MEDIUM` findings.
|
||||
- **Grade A (🟢 HEALTHY)**: 0 findings across all vectors.
|
||||
|
||||
Calculate `Backup Protection Rate %`: $$\text{Protection Rate} =
|
||||
\frac{\text{Total Instances} - \text{Unprotected Instances}}{\text{Total
|
||||
Instances}} \times 100$$
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
### 4. Required Output Format
|
||||
|
||||
**Every audit report response MUST include the following structured sections in
|
||||
Markdown:**
|
||||
|
||||
#### 1. Executive Posture Scorecard
|
||||
|
||||
```markdown
|
||||
## Executive Posture Scorecard: `{project_id}`
|
||||
|
||||
| Metric | Status | Details |
|
||||
| :--- | :--- | :--- |
|
||||
| **Overall Health Posture** | **[🔴 CRITICAL RISK / 🟠 ELEVATED RISK / 🟡 MODERATE / 🟢 HEALTHY]** | [Grade F / C / B / A] |
|
||||
| **Instances Audited** | `[count]` | Total Filestore instances evaluated |
|
||||
| **Backup Protection Rate** | **[pct]%** | `[protected]/[total]` instances have active backups |
|
||||
| **Critical & High Security Findings** | `[crit] Critical, [high] High` | Open exports (0.0.0.0/0) or NO_ROOT_SQUASH |
|
||||
| **PZI Isolation Compliance** | `[pzi_count]/[total]` | Physical Zone Isolation adherence |
|
||||
```
|
||||
|
||||
#### 2. Priority Findings & Remediation Matrix
|
||||
|
||||
List findings ranked by severity (`CRITICAL` $\to$ `HIGH` $\to$ `MEDIUM` $\to$
|
||||
`LOW`):
|
||||
|
||||
```markdown
|
||||
## Priority Findings & Remediation Matrix
|
||||
|
||||
| Severity | Instance ID | Category | Finding Description | Remediation Plan |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| 🚨 **CRITICAL** | `[instance]` | Security | [Description] | [Remediation] |
|
||||
| ⚠️ **HIGH** | `[instance]` | Disaster Recovery | [Description] | [Remediation] |
|
||||
```
|
||||
|
||||
#### 3. Filestore Instance Inventory & Compliance Status
|
||||
|
||||
```markdown
|
||||
## Filestore Instance Inventory & Compliance Status
|
||||
|
||||
| Instance ID | Location | Tier | Capacity | Reserved CIDR | Write IOPS | Throughput | PZI | PZS | Backups | Latest Backup |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| `[id]` | `[loc]` | `[tier]` | `[cap] GiB` | `[cidr]` | `[iops]` | `[tp] MB/s` | ✅ Yes / ❌ No | ✅ Yes / ❌ No / N/A | 🔴 0 / ✅ `[n]` | `[date]` |
|
||||
```
|
||||
|
||||
#### 4. Automated Remediation & Mandatory Confirmation Gate
|
||||
|
||||
If any unprotected instances are discovered, display attributed backup creation
|
||||
commands and conclude with an explicit confirmation request:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore backups create [INSTANCE]-backup-[DATE] \
|
||||
--project=[PROJECT_ID] \
|
||||
--instance=[INSTANCE] \
|
||||
--file-share=[SHARE] \
|
||||
[--instance-zone=[ZONE] | --instance-location=[REGION]] \
|
||||
--region=[BACKUP_REGION]
|
||||
```
|
||||
|
||||
> **Confirmation Required**: Would you like me to proceed with creating baseline
|
||||
> backups for the unprotected instances? Please confirm to proceed.
|
||||
|
||||
**CRITICAL RATIONALE**: Do NOT execute backup creation without explicit user
|
||||
confirmation. Confirmation is strictly required before executing any mutation or
|
||||
backup creation commands in order to prevent unintended operational disruption,
|
||||
unwanted resource allocation, or unexpected backup storage billing.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Reference Directory
|
||||
|
||||
For progressive disclosure and deep architectural guidance, consult the
|
||||
following references:
|
||||
|
||||
- [Security & Access Governance](references/security-access-governance.md)
|
||||
- [Disaster Recovery & Backup Governance](references/backup-dr-governance.md)
|
||||
- [Zone Isolation (PZI) & Reliability (PZS)](references/zone-isolation-pzi-pzs.md)
|
||||
- [Audit Rules & Scoring Matrix](references/audit-rules-matrix.md)
|
||||
- [Standalone Python Audit Script](scripts/filestore_audit.py)
|
||||
@@ -0,0 +1,161 @@
|
||||
# Filestore Audit Rules & Scoring Matrix
|
||||
|
||||
This reference specifies the grading rubrics, severity classification matrix,
|
||||
metric calculations, and table schemas used to evaluate Filestore fleet health.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Table of Contents
|
||||
|
||||
| Section | Line hints |
|
||||
| :--- | :--- |
|
||||
| [1. Severity Classification Matrix](#1-severity-classification-matrix) | Lines 22-70 |
|
||||
| [2. Overall Health Posture & Grading Algorithm](#2-overall-health-posture--grading-algorithm) | Lines 73-106 |
|
||||
| [3. Fleet Metrics Calculation Formulas](#3-fleet-metrics-calculation-formulas) | Lines 109-120 |
|
||||
| [4. Standard Report Schemas](#4-standard-report-schemas) | Lines 123-162 |
|
||||
| • [4.1 Executive Posture Scorecard](#41-executive-posture-scorecard) | Lines 125-137 |
|
||||
| • [4.2 Priority Findings & Remediation Matrix](#42-priority-findings--remediation-matrix) | Lines 139-151 |
|
||||
| • [4.3 Instance Inventory & Compliance Status Table](#43-instance-inventory--compliance-status-table) | Lines 153-162 |
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 1. Severity Classification Matrix
|
||||
|
||||
Every finding identified during the audit must be categorized into one of four
|
||||
deterministic severity levels:
|
||||
|
||||
| Severity | Emoji Badge | Criteria / | Remediation SLA |
|
||||
: : : Triggers : :
|
||||
| :------------- | :------------- | :----------------- | :------------------- |
|
||||
| **`CRITICAL`** | 🚨 **CRITICAL** | • `0.0.0.0/0` or | Immediate (within 24 |
|
||||
: : : `\:\:/0` in : hours) :
|
||||
: : : `ipRanges` with : :
|
||||
: : : `accessMode\: : :
|
||||
: : : READ_WRITE`<br>• : :
|
||||
: : : `NO_ROOT_SQUASH` : :
|
||||
: : : paired with open : :
|
||||
: : : network exposure : :
|
||||
| **`HIGH`** | ⚠️ **HIGH** | • Zero backups on | Urgent (within 72 |
|
||||
: : : active file share : hours) :
|
||||
: : : (`backup_count == : :
|
||||
: : : 0`)<br>• : :
|
||||
: : : `NO_ROOT_SQUASH` : :
|
||||
: : : on internal VPC : :
|
||||
: : : subnets<br>• : :
|
||||
: : : `0.0.0.0/0` with : :
|
||||
: : : `accessMode\: : :
|
||||
: : : READ_ONLY`<br>• : :
|
||||
: : : Multi-zone tier : :
|
||||
: : : (`REGIONAL` / : :
|
||||
: : : `ENTERPRISE`) with : :
|
||||
: : : `satisfiesPzs\: : :
|
||||
: : : false` : :
|
||||
| **`MEDIUM`** | ℹ️ **MEDIUM** | • Stale backup | Planned (next |
|
||||
: : : exceeding SLA : maintenance cycle) :
|
||||
: : : threshold (> 7 : :
|
||||
: : : days)<br>• Default : :
|
||||
: : : open VPC export : :
|
||||
: : : (no explicit : :
|
||||
: : : `nfsExportOptions` : :
|
||||
: : : defined)<br>• : :
|
||||
: : : Zonal instance : :
|
||||
: : : with : :
|
||||
: : : `satisfiesPzi\: : :
|
||||
: : : false` : :
|
||||
| **`LOW`** | 🟢 **LOW** | • Informational | Advisory |
|
||||
: : : notices, : :
|
||||
: : : non-blocking : :
|
||||
: : : configuration : :
|
||||
: : : advice : :
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 2. Overall Health Posture & Grading Algorithm
|
||||
|
||||
The audit engine evaluates the aggregate findings across all instances in the
|
||||
project and assigns a single **Overall Health Posture Grade**:
|
||||
|
||||
$$\text{Total Critical} = \sum \text{Findings with Severity }
|
||||
\mathbf{CRITICAL}$$ $$\text{Total High} = \sum \text{Findings with Severity }
|
||||
\mathbf{HIGH}$$ $$\text{Total Medium} = \sum \text{Findings with Severity }
|
||||
\mathbf{MEDIUM}$$
|
||||
|
||||
### Posture Grade Determination
|
||||
|
||||
1. **Grade F (🔴 CRITICAL RISK)**:
|
||||
- *Condition*: $\text{Total Critical} \ge 1$
|
||||
- *Description*: Severe security exposures detected (such as
|
||||
world-accessible NFS shares or root escalation risks). Requires
|
||||
immediate remediation.
|
||||
2. **Grade C (🟠 ELEVATED RISK)**:
|
||||
- *Condition*: $\text{Total Critical} = 0 \text{ AND } \text{Total High}
|
||||
\ge 1$
|
||||
- *Description*: High-risk operational vulnerabilities present
|
||||
(unprotected instances without backups or cross-zone physical separation
|
||||
failures).
|
||||
3. **Grade B (🟡 MODERATE)**:
|
||||
- *Condition*: $\text{Total Critical} = 0 \text{ AND } \text{Total High} =
|
||||
0 \text{ AND } \text{Total Medium} \ge 1$
|
||||
- *Description*: Moderate configuration gaps (stale backups exceeding SLA,
|
||||
default VPC export rules, or missing PZI isolation).
|
||||
4. **Grade A (🟢 HEALTHY)**:
|
||||
- *Condition*: $\text{Total Critical} = 0 \text{ AND } \text{Total High} =
|
||||
0 \text{ AND } \text{Total Medium} = 0$
|
||||
- *Description*: All instances fully compliant with backup SLA, security
|
||||
export rules, and zone isolation standards.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 3. Fleet Metrics Calculation Formulas
|
||||
|
||||
- **Total Instances Audited**: $N_{\text{total}}$ (Count of all discovered
|
||||
instances in the project).
|
||||
- **Unprotected Instances**: $N_{\text{unprotected}}$ (Count of instances
|
||||
where `backup_count == 0`).
|
||||
- **Backup Protection Rate (%)**: $$\text{Backup Protection Rate} =
|
||||
\begin{cases} 100.0\% & \text{if } N_{\text{total}} = 0 \\ \left(
|
||||
\frac{N_{\text{total}} - N_{\text{unprotected}}}{N_{\text{total}}} \right)
|
||||
\times 100 & \text{if } N_{\text{total}} > 0 \end{cases}$$
|
||||
- **PZI Compliance Count**: Number of instances with `satisfiesPzi == true`.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 4. Standard Report Schemas
|
||||
|
||||
### 4.1 Executive Posture Scorecard
|
||||
|
||||
```markdown
|
||||
## Executive Posture Scorecard: `{project_id}`
|
||||
|
||||
| Metric | Status | Details |
|
||||
| :--- | :--- | :--- |
|
||||
| **Overall Health Posture** | **`{status_badge}`** | `{posture_grade}` |
|
||||
| **Instances Audited** | `{total_instances}` | Total Filestore instances evaluated |
|
||||
| **Backup Protection Rate** | **`{backup_coverage_pct}%`** | `{protected}/{total_instances}` instances have active backups |
|
||||
| **Critical & High Security Findings** | `{crit_count} Critical, {high_count} High` | Open exports, root squash, or missing backups |
|
||||
| **PZI Isolation Compliance** | `{pzi_count}/{total_instances}` | Physical Zone Isolation adherence |
|
||||
```
|
||||
|
||||
### 4.2 Priority Findings & Remediation Matrix
|
||||
|
||||
Sorted strictly in descending order of severity (`CRITICAL` $\to$ `HIGH` $\to$
|
||||
`MEDIUM` $\to$ `LOW`):
|
||||
|
||||
```markdown
|
||||
## Priority Findings & Remediation Matrix
|
||||
|
||||
| Severity | Instance ID | Category | Finding Description | Remediation Plan |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| 🚨 **CRITICAL** | `[instance]` | Security | [Clear description of vulnerability] | [Specific remediation action] |
|
||||
| ⚠️ **HIGH** | `[instance]` | Disaster Recovery | [Clear description of backup gap] | [Attributed gcloud command or MCP action] |
|
||||
```
|
||||
|
||||
### 4.3 Instance Inventory & Compliance Status Table
|
||||
|
||||
```markdown
|
||||
## Filestore Instance Inventory & Compliance Status
|
||||
|
||||
| Instance ID | Location | Tier | Capacity | Reserved CIDR | Write IOPS | Throughput | PZI | PZS | Backups | Latest Backup |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| `[name]` | `[zone/region]` | `[tier]` | `[capacity] GiB` | `[cidr]` | `[iops]` | `[mb_s] MB/s` | ✅ Yes / ❌ No | ✅ Yes / ❌ No / N/A | 🔴 0 / ✅ `[count]` | `[date]` |
|
||||
```
|
||||
@@ -0,0 +1,179 @@
|
||||
# Filestore Disaster Recovery & Backup Governance Reference
|
||||
|
||||
This reference covers disaster recovery (DR) standards, backup architecture, SLA
|
||||
thresholds, and remediation commands for Google Cloud Filestore.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Table of Contents
|
||||
|
||||
| Section | Line hints |
|
||||
| :--- | :--- |
|
||||
| [1. Filestore Backup Architecture](#1-filestore-backup-architecture) | Lines 29-43 |
|
||||
| [2. Disaster Recovery Audit Checks & Thresholds](#2-disaster-recovery-audit-checks--thresholds) | Lines 45-65 |
|
||||
| • [2.1 Unprotected Instances (Zero Backups)](#21-unprotected-instances-zero-backups) | Lines 47-55 |
|
||||
| • [2.2 Stale Backups (Exceeded SLA Threshold)](#22-stale-backups-exceeded-sla-threshold) | Lines 57-65 |
|
||||
| [3. Backup Discovery Across Multi-Runtime Interfaces](#3-backup-discovery-across-multi-runtime-interfaces) | Lines 68-95 |
|
||||
| • [gcloud CLI](#gcloud-cli-project-wide-discovery) | Lines 70-82 |
|
||||
| • [MCP Tool Discovery](#mcp-tool-discovery) | Lines 84-88 |
|
||||
| • [Direct REST API](#direct-rest-api) | Lines 90-95 |
|
||||
| [4. Matching Backups to Instances](#4-matching-backups-to-instances) | Lines 98-109 |
|
||||
| [5. Automated Remediation Commands (Backup Creation)](#5-automated-remediation-commands-backup-creation) | Lines 111-169 |
|
||||
| • [For Zonal Instances](#for-zonal-instances-basic_hdd-basic_ssd-zonal) | Lines 116-141 |
|
||||
| • [For Regional / Multi-Zone Instances](#for-regional--multi-zone-instances-regional-enterprise) | Lines 143-155 |
|
||||
| • [MCP create_backup Payload](#mcp-create_backup-payload) | Lines 157-169 |
|
||||
| [6. Safety & User Confirmation Requirement](#6-safety--user-confirmation-requirement) | Lines 172-180 |
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 1. Filestore Backup Architecture
|
||||
|
||||
A Filestore backup is a point-in-time snapshot of an instance file share that is
|
||||
stored independently from the source Filestore instance cluster:
|
||||
|
||||
- **Independent Failure Domain**: Backups are stored in Google Cloud regional
|
||||
storage facilities. If the source Filestore instance or its datacenter
|
||||
becomes unavailable, backups remain durable and accessible.
|
||||
- **Cross-Region Restoration**: Backups can be restored to a new Filestore
|
||||
instance in the same region or in a different region, enabling geographic
|
||||
disaster recovery.
|
||||
- **Incremental Storage**: Filestore backups share common storage blocks
|
||||
across successive backups of the same file share, minimizing storage costs.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 2. Disaster Recovery Audit Checks & Thresholds
|
||||
|
||||
### 2.1 Unprotected Instances (Zero Backups)
|
||||
|
||||
- **Condition**: An instance has 0 backups in the project.
|
||||
- **Severity**: **`HIGH`**
|
||||
- **Impact**: Accidental volume deletion, ransomware encryption, or physical
|
||||
hardware catastrophe will cause unrecoverable data loss.
|
||||
- **Remediation**: Create an immediate baseline backup of the primary file
|
||||
share.
|
||||
|
||||
### 2.2 Stale Backups (Exceeded SLA Threshold)
|
||||
|
||||
- **Condition**: The most recent backup for an instance is older than
|
||||
`stale_backup_days` (default: **7 days**).
|
||||
- **Severity**: **`MEDIUM`**
|
||||
- **Impact**: Violates Recovery Point Objective (RPO) guarantees. Data
|
||||
modified since the last backup cannot be restored.
|
||||
- **Remediation**: Trigger an ad-hoc backup and configure automated scheduled
|
||||
backups using Cloud Scheduler and Cloud Run or Cloud Functions.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 3. Backup Discovery Across Multi-Runtime Interfaces
|
||||
|
||||
### `gcloud` CLI (Project-Wide Discovery)
|
||||
|
||||
To discover all backups across all regions in a project, run `gcloud filestore
|
||||
backups list` without regional restrictions:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore backups list --project="{project_id}" --format="json"
|
||||
```
|
||||
|
||||
*(Critical: Do NOT pass `--location=-` to `gcloud filestore backups list`; this
|
||||
flag is unsupported and causes command failure).*
|
||||
|
||||
### MCP Tool Discovery
|
||||
|
||||
```json
|
||||
filestore.list_backups({"parent": "projects/{project_id}/locations/-"})
|
||||
```
|
||||
|
||||
### Direct REST API
|
||||
|
||||
```
|
||||
GET https://file.googleapis.com/v1/projects/{project_id}/locations/-/backups
|
||||
User-Agent: gcs-skills/1.0 (skill:google-cloud-filestore-auditing)
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 4. Matching Backups to Instances
|
||||
|
||||
Backups must be matched to instances using the full canonical resource URI
|
||||
rather than short names to avoid cross-zone collisions:
|
||||
|
||||
- **Canonical Instance URI**:
|
||||
`projects/{project_id}/locations/{location}/instances/{instance_name}`
|
||||
- **Source Instance Property in Backup**: Each backup resource returns
|
||||
`sourceInstance`. A backup belongs to an instance if:
|
||||
`backup.sourceInstance == instance.name`
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 5. Automated Remediation Commands (Backup Creation)
|
||||
|
||||
When generating remediation commands for unprotected instances, determine the
|
||||
correct location flags based on instance tier:
|
||||
|
||||
### For Zonal Instances (`BASIC_HDD`, `BASIC_SSD`, `ZONAL`)
|
||||
|
||||
Zonal instances require `--instance-zone`:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore backups create {instance_name}-backup-$(date +%Y%m%d) \
|
||||
--project={project_id} \
|
||||
--instance={instance_name} \
|
||||
--file-share={file_share_name} \
|
||||
--instance-zone={instance_zone} \
|
||||
--region={backup_region}
|
||||
```
|
||||
|
||||
*Example*:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore backups create nfs-prod-backup-20260903 \
|
||||
--project=prod-storage \
|
||||
--instance=nfs-prod \
|
||||
--file-share=vol1 \
|
||||
--instance-zone=us-central1-b \
|
||||
--region=us-central1
|
||||
```
|
||||
|
||||
### For Regional / Multi-Zone Instances (`REGIONAL`, `ENTERPRISE`)
|
||||
|
||||
Regional instances require `--instance-location`:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore backups create {instance_name}-backup-$(date +%Y%m%d) \
|
||||
--project={project_id} \
|
||||
--instance={instance_name} \
|
||||
--file-share={file_share_name} \
|
||||
--instance-location={instance_region} \
|
||||
--region={backup_region}
|
||||
```
|
||||
|
||||
### MCP `create_backup` Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"parent": "projects/{project_id}/locations/{backup_region}",
|
||||
"backupId": "{instance_name}-backup-20260903",
|
||||
"backup": {
|
||||
"sourceInstance": "projects/{project_id}/locations/{instance_location}/instances/{instance_name}",
|
||||
"sourceFileShare": "{file_share_name}",
|
||||
"description": "Baseline DR backup generated by google-cloud-filestore-auditing"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 6. Safety & User Confirmation Requirement
|
||||
|
||||
**MANDATORY GUARDRAIL**: The skill must never execute backup creation commands
|
||||
without prior user confirmation. Present the dry-run command list and conclude
|
||||
with an explicit prompt:
|
||||
|
||||
> *"Would you like me to proceed with creating baseline backups for the
|
||||
> unprotected instances? Please confirm to execute."*
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
# Filestore Security & Access Governance Reference
|
||||
|
||||
This reference provides governance rules, security risk criteria, and
|
||||
remediation guidance for Google Cloud Filestore access configurations.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Table of Contents
|
||||
|
||||
| Section | Line hints |
|
||||
| :--- | :--- |
|
||||
| [1. NFSv3 Access Control Overview](#1-nfsv3-access-control-overview) | Lines 24-42 |
|
||||
| [2. Security Findings & Risk Classifications](#2-security-findings--risk-classifications) | Lines 45-87 |
|
||||
| • [2.1 Overly Permissive Network Exposure (0.0.0.0/0)](#21-overly-permissive-network-exposure-00000) | Lines 47-60 |
|
||||
| • [2.2 Missing Root Squashing (NO_ROOT_SQUASH)](#22-missing-root-squashing-no_root_squash) | Lines 62-75 |
|
||||
| • [2.3 Default Open VPC Exports (No Explicit Rules)](#23-default-open-vpc-exports-no-explicit-rules) | Lines 77-87 |
|
||||
| [3. Recommended Remediation Configurations](#3-recommended-remediation-configurations) | Lines 90-126 |
|
||||
| • [Recommended JSON Configuration](#recommended-json-configuration) | Lines 92-109 |
|
||||
| • [Remediation via gcloud CLI](#remediation-via-gcloud-cli) | Lines 111-126 |
|
||||
| [4. IAM Governance & Least Privilege](#4-iam-governance--least-privilege) | Lines 129-141 |
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 1. NFSv3 Access Control Overview
|
||||
|
||||
Google Cloud Filestore instances export file shares using Network File System
|
||||
version 3 (NFSv3). Because NFSv3 relies on client-reported POSIX user IDs (UIDs)
|
||||
and group IDs (GIDs) without cryptographic Kerberos authentication by default,
|
||||
network-level export options are the primary line of defense.
|
||||
|
||||
Access permissions are configured on the file share via `nfsExportOptions`. Each
|
||||
rule within `nfsExportOptions` defines:
|
||||
|
||||
- **`ipRanges`**: A list of IPv4 or IPv6 CIDR blocks permitted to connect.
|
||||
- **`accessMode`**: Allowed access level (`READ_WRITE` or `READ_ONLY`).
|
||||
- **`squashMode`**: User ID mapping behavior (`NO_ROOT_SQUASH` or
|
||||
`ROOT_SQUASH`).
|
||||
- **`anonUid`**: The target POSIX UID when squashing root (default: `65534` /
|
||||
`nobody`).
|
||||
- **`anonGid`**: The target POSIX GID when squashing root (default: `65534` /
|
||||
`nogroup`).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 2. Security Findings & Risk Classifications
|
||||
|
||||
### 2.1 Overly Permissive Network Exposure (`0.0.0.0/0`)
|
||||
|
||||
- **Condition**: An export rule contains `0.0.0.0/0`, `0.0.0.0`, or `::/0` in
|
||||
its `ipRanges`.
|
||||
- **Severity**:
|
||||
- **`CRITICAL`** if `accessMode` is `READ_WRITE`.
|
||||
- **`HIGH`** if `accessMode` is `READ_ONLY`.
|
||||
- **Risk**: Any host that can reach the Filestore IP (e.g., peered VPCs,
|
||||
shared transit networks, compromised VMs, or interconnects) can mount the
|
||||
file share.
|
||||
- **Remediation**: Replace `0.0.0.0/0` with explicit, minimal CIDRs
|
||||
corresponding to authorized VPC subnets, GKE node pools, or private consumer
|
||||
IP ranges.
|
||||
|
||||
### 2.2 Missing Root Squashing (`NO_ROOT_SQUASH`)
|
||||
|
||||
- **Condition**: An export rule defines `squashMode: NO_ROOT_SQUASH`.
|
||||
- **Severity**:
|
||||
- **`CRITICAL`** if paired with open network exposure (`0.0.0.0/0`) or
|
||||
public access.
|
||||
- **`HIGH`** when restricted to internal VPC subnets.
|
||||
- **Risk**: Clients connecting with local `root` (UID 0) retain full superuser
|
||||
privileges on the Filestore share. Any compromised container or VM running
|
||||
as root can overwrite system binaries, read sensitive data, or alter
|
||||
security file modes across the entire share.
|
||||
- **Remediation**: Configure `squashMode: ROOT_SQUASH`. Clients connecting as
|
||||
root are automatically remapped to `anonUid: 65534` (`nobody`), enforcing
|
||||
least-privilege POSIX semantics.
|
||||
|
||||
### 2.3 Default Open VPC Exports (No Explicit Rules)
|
||||
|
||||
- **Condition**: `nfsExportOptions` is empty or missing from the instance
|
||||
specification.
|
||||
- **Severity**: **`MEDIUM`**
|
||||
- **Risk**: By default in Google Cloud Console and the GCP API, instances
|
||||
without explicit export rules allow all compute instances in the connected
|
||||
VPC network to mount the share with **`NO_ROOT_SQUASH`** and
|
||||
**`READ_WRITE`** permissions.
|
||||
- **Remediation**: Add explicit `nfsExportOptions` to the instance
|
||||
specification under advanced access controls.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 3. Recommended Remediation Configurations
|
||||
|
||||
### Recommended JSON Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"nfsExportOptions": [
|
||||
{
|
||||
"ipRanges": [
|
||||
"10.128.0.0/20"
|
||||
],
|
||||
"accessMode": "READ_WRITE",
|
||||
"squashMode": "ROOT_SQUASH",
|
||||
"anonUid": 65534,
|
||||
"anonGid": 65534
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Remediation via `gcloud` CLI
|
||||
|
||||
To update export options on an existing instance, provide the export
|
||||
configuration via `--flags-file` or the direct flags:
|
||||
|
||||
```bash
|
||||
CLOUDSDK_METRICS_ENVIRONMENT="gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)" \
|
||||
gcloud filestore instances update [INSTANCE_ID] \
|
||||
--project=[PROJECT_ID] \
|
||||
--zone=[ZONE] \
|
||||
--file-share=name=[SHARE_NAME],nfs-export-options='[{"ip-ranges":["10.128.0.0/20"],"access-mode":"READ_WRITE","squash-mode":"ROOT_SQUASH"}]'
|
||||
```
|
||||
|
||||
*(Note: Always prompt for user confirmation before executing modifications on
|
||||
active file shares to avoid disrupting ongoing client mounts).*
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 4. IAM Governance & Least Privilege
|
||||
|
||||
Filestore access control at the GCP project level is governed by Cloud IAM. The
|
||||
principle of least privilege must be applied:
|
||||
|
||||
- **`roles/file.admin`**: Full administrative control (create, modify, delete
|
||||
instances, backups, and snapshots). Restrict strictly to storage and
|
||||
platform administrators. Never grant to `allUsers` or
|
||||
`allAuthenticatedUsers`.
|
||||
- **`roles/file.editor`**: Can modify instances, trigger resizes, and
|
||||
create/restore backups.
|
||||
- **`roles/file.viewer`**: Read-only access to instance metadata, export
|
||||
rules, and backup statuses. Recommended role for audit runners.
|
||||
@@ -0,0 +1,196 @@
|
||||
# Zone Isolation (PZI) & Reliability (PZS) Compliance Reference
|
||||
|
||||
This reference explains Physical Zone Isolation (PZI), Physical Zone Separation
|
||||
(PZS), and architectural reliability standards for Google Cloud Filestore.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## Table of Contents
|
||||
|
||||
| Section | Line hints |
|
||||
| :--- | :--- |
|
||||
| [1. Physical Zone Isolation (PZI)](#1-physical-zone-isolation-pzi) | Lines 23-48 |
|
||||
| [2. Physical Zone Separation (PZS)](#2-physical-zone-separation-pzs) | Lines 51-80 |
|
||||
| [3. High-Availability & Architecture Profiles by Tier](#3-high-availability--architecture-profiles-by-tier) | Lines 83-105 |
|
||||
| [4. Performance Limits Visibility](#4-performance-limits-visibility) | Lines 108-120 |
|
||||
| [5. Mock Fleet Definitions (For No-Command Evaluations)](#5-mock-fleet-definitions-for-no-command-evaluations) | Lines 123-197 |
|
||||
| • [Instance 1: nfs-prod-1](#instance-1-nfs-prod-1) | Lines 132-154 |
|
||||
| • [Instance 2: nfs-prod-2](#instance-2-nfs-prod-2) | Lines 156-175 |
|
||||
| • [Instance 3: nfs-prod-3](#instance-3-nfs-prod-3) | Lines 177-197 |
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 1. Physical Zone Isolation (PZI)
|
||||
|
||||
### Concept
|
||||
|
||||
Physical Zone Isolation (PZI) verifies that a zonal Google Cloud resource (such
|
||||
as a Filestore instance hosted in `us-central1-a`) has all of its compute,
|
||||
storage, network routing, and power infrastructure confined strictly to a single
|
||||
physical failure domain within that datacenter.
|
||||
|
||||
### Operational Importance
|
||||
|
||||
If an infrastructure incident (such as an uninterruptible power supply failure
|
||||
or Top-of-Rack switch fault) occurs in Zone A, a PZI-compliant resource has zero
|
||||
hidden runtime dependencies on Zone B or Zone C. Failures remain confined to
|
||||
that single physical domain.
|
||||
|
||||
### API Representation
|
||||
|
||||
The Filestore Instance API exposes `satisfiesPzi` as an output-only boolean:
|
||||
|
||||
- **`true`**: The instance is provisioned in a datacenter domain verified for
|
||||
Physical Zone Isolation.
|
||||
- **`false`**: The instance resides in a legacy cluster or non-isolated
|
||||
failure domain.
|
||||
- **Audit Severity**: **`MEDIUM`** if `satisfiesPzi: false`.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 2. Physical Zone Separation (PZS)
|
||||
|
||||
### Concept
|
||||
|
||||
Physical Zone Separation (PZS) applies to multi-zone and regional Filestore
|
||||
resources (such as `REGIONAL` or `ENTERPRISE` tiers, which provide 99.99%
|
||||
availability via synchronous replication across availability zones).
|
||||
|
||||
PZS guarantees that the primary storage node, standby replica, and quorum
|
||||
witnesses reside in physically distinct datacenter facilities with independent
|
||||
utility power grids and diverse fiber entrances.
|
||||
|
||||
### Operational Importance
|
||||
|
||||
If a major physical facility incident (such as a municipal power outage,
|
||||
building fire, or localized flooding) impacts one datacenter facility, the
|
||||
secondary replica in the physically separated zone is completely unaffected,
|
||||
ensuring automated failover without data loss.
|
||||
|
||||
### API Representation
|
||||
|
||||
The Filestore Instance API exposes `satisfiesPzs` as an output-only boolean:
|
||||
|
||||
- **`true`**: Active and standby replicas reside in confirmed physically
|
||||
separated datacenter buildings.
|
||||
- **`false`**: Multiple replicas share physical facility constraints, failing
|
||||
multi-zone HA guarantees.
|
||||
- **Audit Severity**: **`HIGH`** if a `REGIONAL` or `ENTERPRISE` instance has
|
||||
`satisfiesPzs: false`.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 3. High-Availability & Architecture Profiles by Tier
|
||||
|
||||
| Tier (UI / | Architecture | Replication | PZI Applicable? | PZS Applicable? | Supported |
|
||||
: API) : Profile : Model : : : Resizing :
|
||||
| :------------ | :----------- | :---------- | :--------------: | :--------------: | :--------: |
|
||||
| **Basic HDD** | Single | None | Yes | N/A | Scale-Up |
|
||||
: (`BASIC_HDD`) : Compute : (Zonal) : (`satisfiesPzi`) : : Only :
|
||||
: : Engine VM + : : : : :
|
||||
: : PD-Standard : : : : :
|
||||
| **Basic SSD** | Single | None | Yes | N/A | Scale-Up |
|
||||
: (`BASIC_SSD`) : Compute : (Zonal) : (`satisfiesPzi`) : : Only :
|
||||
: : Engine VM + : : : : :
|
||||
: : PD-SSD : : : : :
|
||||
| **Zonal** | ECFS | Intra-Zone | Yes | N/A | Scale-Up & |
|
||||
: (`ZONAL`) : Multi-Node : Replicated : (`satisfiesPzi`) : : Scale-Down :
|
||||
: : Cluster : : : : :
|
||||
: : (Single : : : : :
|
||||
: : Zone) : : : : :
|
||||
| **Regional** | ECFS | Cross-Zone | Yes | Yes | Scale-Up & |
|
||||
: (`REGIONAL` / : Multi-Node : Synchronous : (`satisfiesPzi`) : (`satisfiesPzs`) : Scale-Down :
|
||||
: `ENTERPRISE`) : Cluster : : : : :
|
||||
: : (Multi-Zone) : : : : :
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 4. Performance Limits Visibility
|
||||
|
||||
The Filestore Instance API provides baseline performance ceilings under
|
||||
`performanceLimits`:
|
||||
|
||||
- **`maxWriteIops`**: Maximum write I/O operations per second.
|
||||
- **`maxReadThroughputBps`**: Maximum read throughput in bytes per second.
|
||||
Convert to MB/s using: $$\text{Throughput (MB/s)} = \left\lfloor
|
||||
\frac{\text{maxReadThroughputBps}}{1024 \times 1024} \right\rfloor$$
|
||||
|
||||
Display these values in the Instance Inventory Table to provide complete
|
||||
operational context.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
## 5. Mock Fleet Definitions (For No-Command Evaluations)
|
||||
|
||||
When running in headless evaluation suites, testing under "no-command"
|
||||
constraints, or when the GCP API is not directly reachable, the following
|
||||
reference fleet in project `audit-prod` serves as the golden evaluation
|
||||
baseline:
|
||||
|
||||
### Project `audit-prod` Mock Fleet
|
||||
|
||||
#### Instance 1: `nfs-prod-1`
|
||||
|
||||
- **Resource URI**:
|
||||
`projects/audit-prod/locations/us-central1-b/instances/nfs-prod-1`
|
||||
- **Location**: `us-central1-b`
|
||||
- **Tier**: `BASIC_SSD`
|
||||
- **Capacity**: 2560 GiB (2.5 TiB)
|
||||
- **File Share**: `vol1`
|
||||
- **Network**: `default` (Reserved CIDR: `10.0.0.0/29`)
|
||||
- **Export Rules**:
|
||||
- Rule 1: `ipRanges: ["0.0.0.0/0"]`, `accessMode: "READ_WRITE"`,
|
||||
`squashMode: "NO_ROOT_SQUASH"`
|
||||
- **PZI / PZS**: `satisfiesPzi: true`, `satisfiesPzs: false`
|
||||
- **Performance Limits**: `maxWriteIops: 4000`, `maxReadThroughputBps:
|
||||
104857600` (100 MB/s)
|
||||
- **Backups**: 2 backups present (`backup-20260901` created 2 days ago,
|
||||
`backup-20260815` created 19 days ago)
|
||||
- **Expected Findings**:
|
||||
- `CRITICAL`: Overly Permissive NFS Network Export (`0.0.0.0/0` with
|
||||
`READ_WRITE`).
|
||||
- `CRITICAL`: Missing Root Squashing (`NO_ROOT_SQUASH` paired with
|
||||
`0.0.0.0/0`).
|
||||
|
||||
#### Instance 2: `nfs-prod-2`
|
||||
|
||||
- **Resource URI**:
|
||||
`projects/audit-prod/locations/us-central1-c/instances/nfs-prod-2`
|
||||
- **Location**: `us-central1-c`
|
||||
- **Tier**: `BASIC_HDD`
|
||||
- **Capacity**: 1024 GiB (1 TiB)
|
||||
- **File Share**: `vol1`
|
||||
- **Network**: `default` (Reserved CIDR: `10.0.1.0/29`)
|
||||
- **Export Rules**:
|
||||
- Rule 1: `ipRanges: ["10.128.0.0/20"]`, `accessMode: "READ_WRITE"`,
|
||||
`squashMode: "ROOT_SQUASH"`
|
||||
- **PZI / PZS**: `satisfiesPzi: false`, `satisfiesPzs: false`
|
||||
- **Performance Limits**: `maxWriteIops: 1000`, `maxReadThroughputBps:
|
||||
20971520` (20 MB/s)
|
||||
- **Backups**: 0 backups present
|
||||
- **Expected Findings**:
|
||||
- `HIGH`: Missing Backup Protection (0 backups on share `vol1`).
|
||||
- `MEDIUM`: Physical Zone Isolation (PZI) Non-Compliant (`satisfiesPzi:
|
||||
false`).
|
||||
|
||||
#### Instance 3: `nfs-prod-3`
|
||||
|
||||
- **Resource URI**:
|
||||
`projects/audit-prod/locations/us-central1/instances/nfs-prod-3`
|
||||
- **Location**: `us-central1`
|
||||
- **Tier**: `REGIONAL`
|
||||
- **Capacity**: 1024 GiB (1 TiB)
|
||||
- **File Share**: `vol1`
|
||||
- **Network**: `default` (Reserved CIDR: `10.0.2.0/29`)
|
||||
- **Export Rules**:
|
||||
- Rule 1: `ipRanges: ["10.128.0.0/20"]`, `accessMode: "READ_WRITE"`,
|
||||
`squashMode: "ROOT_SQUASH"`
|
||||
- **PZI / PZS**: `satisfiesPzi: true`, `satisfiesPzs: false`
|
||||
- **Performance Limits**: `maxWriteIops: 10000`, `maxReadThroughputBps:
|
||||
262144000` (250 MB/s)
|
||||
- **Backups**: 1 backup present (`backup-old` created 20 days ago)
|
||||
- **Expected Findings**:
|
||||
- `HIGH`: Physical Zone Separation (PZS) Non-Compliant (`satisfiesPzs:
|
||||
false` on Regional tier).
|
||||
- `MEDIUM`: Stale Backup (latest backup is 20 days old, exceeding 7-day
|
||||
SLA).
|
||||
@@ -0,0 +1,651 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Google Cloud Filestore Comprehensive Audit & Remediation Engine.
|
||||
|
||||
Audits Google Cloud Filestore instances across GCP projects for:
|
||||
1. Disaster Recovery & Backup Protection (unprotected shares & stale backups)
|
||||
2. Security & Access Governance (NFS export options, 0.0.0.0/0 exposure,
|
||||
ROOT_SQUASH)
|
||||
3. Reliability & Zone Isolation Compliance (Physical Zone Isolation PZI/PZS,
|
||||
performance limits)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
METRICS_ENV = (
|
||||
"gcs-skills gcs-skills/1.0 (skill:google-cloud-filestore-auditing)"
|
||||
)
|
||||
DEFAULT_STALE_BACKUP_DAYS = 7
|
||||
|
||||
|
||||
def run_command(cmd: List[str]) -> Tuple[int, str, str]:
|
||||
"""Executes a subprocess command with attribution and returns (returncode, stdout, stderr)."""
|
||||
env = os.environ.copy()
|
||||
env["CLOUDSDK_METRICS_ENVIRONMENT"] = METRICS_ENV
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd, capture_output=True, text=True, check=False, env=env
|
||||
)
|
||||
return proc.returncode, proc.stdout, proc.stderr
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
return 1, "", str(e)
|
||||
|
||||
|
||||
def parse_rfc3339_timestamp(ts_str: str) -> Optional[datetime.datetime]:
|
||||
"""Parses RFC3339 timestamp strings to datetime objects."""
|
||||
if not ts_str:
|
||||
return None
|
||||
ts_clean = re.sub(r"\.\d+", "", ts_str).replace("Z", "+00:00")
|
||||
try:
|
||||
return datetime.datetime.fromisoformat(ts_clean)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
class FilestoreAuditor:
|
||||
"""Auditor engine for Google Cloud Filestore fleets."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
project_id: str,
|
||||
instance_id: Optional[str] = None,
|
||||
location: str = "-",
|
||||
stale_backup_days: int = DEFAULT_STALE_BACKUP_DAYS,
|
||||
):
|
||||
self.project_id = project_id
|
||||
self.target_instance = instance_id
|
||||
self.location = location
|
||||
self.stale_backup_days = stale_backup_days
|
||||
|
||||
def fetch_instances(self) -> List[Dict[str, Any]]:
|
||||
"""Fetches all Filestore instances across locations in the project."""
|
||||
cmd = [
|
||||
"gcloud",
|
||||
"filestore",
|
||||
"instances",
|
||||
"list",
|
||||
f"--project={self.project_id}",
|
||||
"--format=json",
|
||||
]
|
||||
rc, stdout, stderr = run_command(cmd)
|
||||
if rc != 0:
|
||||
raise RuntimeError(f"Failed to fetch instances: {stderr.strip()}")
|
||||
try:
|
||||
instances = json.loads(stdout) if stdout.strip() else []
|
||||
if self.target_instance:
|
||||
instances = [
|
||||
i
|
||||
for i in instances
|
||||
if i.get("name", "").split("/")[-1] == self.target_instance
|
||||
]
|
||||
return instances
|
||||
except json.JSONDecodeError:
|
||||
return []
|
||||
|
||||
def fetch_backups(self) -> List[Dict[str, Any]]:
|
||||
"""Fetches all Filestore backups across all regions in the project."""
|
||||
# Note: gcloud filestore backups list without --region lists all backups
|
||||
# across the project. Do NOT pass --location=- as it is unsupported.
|
||||
cmd = [
|
||||
"gcloud",
|
||||
"filestore",
|
||||
"backups",
|
||||
"list",
|
||||
f"--project={self.project_id}",
|
||||
"--format=json",
|
||||
]
|
||||
rc, stdout, stderr = run_command(cmd)
|
||||
if rc != 0:
|
||||
raise RuntimeError(f"Failed to fetch backups: {stderr.strip()}")
|
||||
try:
|
||||
return json.loads(stdout) if stdout.strip() else []
|
||||
except json.JSONDecodeError as e:
|
||||
raise RuntimeError(f"Failed to parse backups JSON output: {e}") from e
|
||||
|
||||
def _audit_disaster_recovery(
|
||||
self,
|
||||
inst_name: str,
|
||||
share_name: str,
|
||||
loc_flag: str,
|
||||
backup_region: str,
|
||||
matched_backups: List[Dict[str, Any]],
|
||||
) -> Tuple[List[Dict[str, Any]], str, Optional[int]]:
|
||||
"""Evaluates disaster recovery and backup coverage for a single instance."""
|
||||
findings: List[Dict[str, Any]] = []
|
||||
backup_count = len(matched_backups)
|
||||
latest_backup_date = "None"
|
||||
days_since_backup = None
|
||||
|
||||
if backup_count == 0:
|
||||
findings.append({
|
||||
"category": "Disaster Recovery",
|
||||
"severity": "HIGH",
|
||||
"check": "Missing Backup Protection",
|
||||
"message": (
|
||||
f"Instance has 0 backups on file share '{share_name}'. "
|
||||
"Disaster recovery is not configured."
|
||||
),
|
||||
"remediation": (
|
||||
f"gcloud filestore backups create {inst_name}-backup-$(date"
|
||||
f" +%Y%m%d) --project={self.project_id} --instance={inst_name}"
|
||||
f" --file-share={share_name} {loc_flag} --region={backup_region}"
|
||||
),
|
||||
})
|
||||
else:
|
||||
parsed_dates = []
|
||||
for b in matched_backups:
|
||||
c_time = parse_rfc3339_timestamp(b.get("createTime", ""))
|
||||
if c_time:
|
||||
parsed_dates.append((c_time, b))
|
||||
|
||||
if parsed_dates:
|
||||
parsed_dates.sort(key=lambda x: x[0], reverse=True)
|
||||
latest_dt, _ = parsed_dates[0]
|
||||
latest_backup_date = latest_dt.strftime("%Y-%m-%d %H:%M UTC")
|
||||
days_since_backup = (
|
||||
datetime.datetime.now(datetime.timezone.utc) - latest_dt
|
||||
).days
|
||||
|
||||
if days_since_backup > self.stale_backup_days:
|
||||
findings.append({
|
||||
"category": "Disaster Recovery",
|
||||
"severity": "MEDIUM",
|
||||
"check": "Stale Backup",
|
||||
"message": (
|
||||
f"Latest backup is {days_since_backup} days old "
|
||||
f"(exceeds SLA threshold of {self.stale_backup_days} days)."
|
||||
),
|
||||
"remediation": (
|
||||
"Take an updated backup snapshot or configure automated "
|
||||
"scheduled backups via Cloud Scheduler."
|
||||
),
|
||||
})
|
||||
|
||||
return findings, latest_backup_date, days_since_backup
|
||||
|
||||
def _audit_security(
|
||||
self, primary_share: Dict[str, Any]
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Evaluates security access governance (NFS exports, root squashing)."""
|
||||
findings: List[Dict[str, Any]] = []
|
||||
export_rules = primary_share.get("nfsExportOptions", [])
|
||||
if not export_rules:
|
||||
findings.append({
|
||||
"category": "Security",
|
||||
"severity": "MEDIUM",
|
||||
"check": "Default Open NFS Export",
|
||||
"message": (
|
||||
"No explicit NFS export options configured. The share defaults"
|
||||
" to open client access within the VPC network with"
|
||||
" NO_ROOT_SQUASH."
|
||||
),
|
||||
"remediation": (
|
||||
"Configure explicit nfsExportOptions restricting ipRanges and "
|
||||
"enforcing ROOT_SQUASH under advanced access control."
|
||||
),
|
||||
})
|
||||
return findings
|
||||
|
||||
for idx, rule in enumerate(export_rules):
|
||||
ip_ranges = rule.get("ipRanges", [])
|
||||
squash_mode = rule.get("squashMode", "SQUASH_MODE_UNSPECIFIED")
|
||||
access_mode = rule.get("accessMode", "ACCESS_MODE_UNSPECIFIED")
|
||||
|
||||
is_world_exposed = any(
|
||||
ip in ["0.0.0.0/0", "0.0.0.0", "::/0"] for ip in ip_ranges
|
||||
)
|
||||
if is_world_exposed:
|
||||
findings.append({
|
||||
"category": "Security",
|
||||
"severity": "CRITICAL" if access_mode == "READ_WRITE" else "HIGH",
|
||||
"check": "Overly Permissive NFS Network Export",
|
||||
"message": (
|
||||
f"Export rule #{idx+1} exposes share to 0.0.0.0/0 (all IPs) "
|
||||
f"with accessMode='{access_mode}'."
|
||||
),
|
||||
"remediation": (
|
||||
"Restrict ipRanges to specific authorized VPC subnet CIDRs or"
|
||||
" GKE node pool IP blocks."
|
||||
),
|
||||
})
|
||||
|
||||
if squash_mode == "NO_ROOT_SQUASH":
|
||||
sec_sev = "CRITICAL" if is_world_exposed else "HIGH"
|
||||
findings.append({
|
||||
"category": "Security",
|
||||
"severity": sec_sev,
|
||||
"check": "Missing Root Squashing (NO_ROOT_SQUASH)",
|
||||
"message": (
|
||||
f"Export rule #{idx+1} has squashMode='NO_ROOT_SQUASH'."
|
||||
" Remote root clients retain superuser UID 0 privileges on"
|
||||
" the share."
|
||||
),
|
||||
"remediation": (
|
||||
"Change squashMode to 'ROOT_SQUASH' and specify anonUid:"
|
||||
" 65534 / anonGid: 65534 to enforce least-privilege POSIX"
|
||||
" mapping."
|
||||
),
|
||||
})
|
||||
|
||||
return findings
|
||||
|
||||
def _audit_compliance(
|
||||
self, inst: Dict[str, Any], tier: str
|
||||
) -> Tuple[List[Dict[str, Any]], bool, bool]:
|
||||
"""Evaluates architectural reliability and zone isolation compliance (PZI/PZS)."""
|
||||
findings: List[Dict[str, Any]] = []
|
||||
satisfies_pzi = inst.get("satisfiesPzi", False)
|
||||
satisfies_pzs = inst.get("satisfiesPzs", False)
|
||||
|
||||
if not satisfies_pzi:
|
||||
findings.append({
|
||||
"category": "Compliance",
|
||||
"severity": "MEDIUM",
|
||||
"check": "Physical Zone Isolation (PZI) Non-Compliant",
|
||||
"message": (
|
||||
"Instance does not satisfy Physical Zone Isolation (PZI) "
|
||||
f"(satisfiesPzi={satisfies_pzi})."
|
||||
),
|
||||
"remediation": (
|
||||
"Provision replacement instances in PZI-compliant zones or"
|
||||
" migrate to Regional tier."
|
||||
),
|
||||
})
|
||||
|
||||
if tier in ["ENTERPRISE", "REGIONAL"] and not satisfies_pzs:
|
||||
findings.append({
|
||||
"category": "Compliance",
|
||||
"severity": "HIGH",
|
||||
"check": "Physical Zone Separation (PZS) Non-Compliant",
|
||||
"message": (
|
||||
"Enterprise/Regional tier instance does not satisfy Physical"
|
||||
f" Zone Separation (satisfiesPzs={satisfies_pzs}). Replicas may"
|
||||
" share physical facilities."
|
||||
),
|
||||
"remediation": (
|
||||
"Verify multi-zone configuration across distinct physical domains"
|
||||
" within the region."
|
||||
),
|
||||
})
|
||||
|
||||
return findings, satisfies_pzi, satisfies_pzs
|
||||
|
||||
def audit_instance(
|
||||
self, inst: Dict[str, Any], project_backups: List[Dict[str, Any]]
|
||||
) -> Dict[str, Any]:
|
||||
"""Runs all audit checks against a single Filestore instance."""
|
||||
full_name = inst.get("name", "")
|
||||
inst_name = full_name.split("/")[-1] if "/" in full_name else full_name
|
||||
parts = full_name.split("/")
|
||||
inst_location = parts[3] if len(parts) >= 4 else "unknown"
|
||||
tier = inst.get("tier", "UNKNOWN")
|
||||
file_shares = inst.get("fileShares", [])
|
||||
networks = inst.get("networks", [])
|
||||
|
||||
primary_share = file_shares[0] if file_shares else {}
|
||||
share_name = primary_share.get("name", "vol1")
|
||||
capacity_gb = primary_share.get("capacityGb", "N/A")
|
||||
|
||||
# Determine location type and backup target region
|
||||
is_regional = (
|
||||
tier in ["ENTERPRISE", "REGIONAL"] or len(inst_location.split("-")) < 3
|
||||
)
|
||||
if is_regional:
|
||||
backup_region = inst_location
|
||||
loc_flag = f"--instance-location={inst_location}"
|
||||
else:
|
||||
loc_parts = inst_location.split("-")
|
||||
backup_region = (
|
||||
f"{loc_parts[0]}-{loc_parts[1]}"
|
||||
if len(loc_parts) >= 2
|
||||
else inst_location
|
||||
)
|
||||
loc_flag = f"--instance-zone={inst_location}"
|
||||
|
||||
# Match backups to instance
|
||||
matched_backups = []
|
||||
for b in project_backups:
|
||||
src_inst = b.get("sourceInstance", "")
|
||||
if src_inst == full_name or (
|
||||
src_inst and full_name.endswith(src_inst.strip("/"))
|
||||
):
|
||||
matched_backups.append(b)
|
||||
|
||||
dr_findings, latest_backup_date, days_since_backup = (
|
||||
self._audit_disaster_recovery(
|
||||
inst_name, share_name, loc_flag, backup_region, matched_backups
|
||||
)
|
||||
)
|
||||
sec_findings = self._audit_security(primary_share)
|
||||
comp_findings, satisfies_pzi, satisfies_pzs = self._audit_compliance(
|
||||
inst, tier
|
||||
)
|
||||
|
||||
findings = dr_findings + sec_findings + comp_findings
|
||||
|
||||
reserved_ip_range = "N/A"
|
||||
connect_mode = "DIRECT_PEERING"
|
||||
if networks:
|
||||
reserved_ip_range = networks[0].get("reservedIpRange", "N/A")
|
||||
connect_mode = networks[0].get("connectMode", "DIRECT_PEERING")
|
||||
|
||||
perf_limits = inst.get("performanceLimits", {})
|
||||
max_write_iops = perf_limits.get("maxWriteIops", "N/A")
|
||||
max_read_throughput_bps = perf_limits.get("maxReadThroughputBps", "0")
|
||||
try:
|
||||
throughput_mb = int(max_read_throughput_bps) // (1024 * 1024)
|
||||
max_throughput = f"{throughput_mb} MB/s" if throughput_mb > 0 else "N/A"
|
||||
except (ValueError, TypeError):
|
||||
max_throughput = "N/A"
|
||||
|
||||
return {
|
||||
"instance_id": inst_name,
|
||||
"location": inst_location,
|
||||
"is_regional": is_regional,
|
||||
"backup_region": backup_region,
|
||||
"tier": tier,
|
||||
"capacity_gb": capacity_gb,
|
||||
"file_share": share_name,
|
||||
"reserved_ip_range": reserved_ip_range,
|
||||
"connect_mode": connect_mode,
|
||||
"max_write_iops": max_write_iops,
|
||||
"max_throughput": max_throughput,
|
||||
"satisfies_pzi": satisfies_pzi,
|
||||
"satisfies_pzs": satisfies_pzs,
|
||||
"backup_count": len(matched_backups),
|
||||
"latest_backup_date": latest_backup_date,
|
||||
"days_since_backup": days_since_backup,
|
||||
"findings": findings,
|
||||
}
|
||||
|
||||
def run_audit(self) -> Dict[str, Any]:
|
||||
"""Executes the full audit across instances and returns structured report."""
|
||||
instances = self.fetch_instances()
|
||||
backups = self.fetch_backups()
|
||||
|
||||
audited_instances = [
|
||||
self.audit_instance(inst, backups) for inst in instances
|
||||
]
|
||||
|
||||
total_instances = len(audited_instances)
|
||||
crit_count = 0
|
||||
high_count = 0
|
||||
med_count = 0
|
||||
for i in audited_instances:
|
||||
for f in i["findings"]:
|
||||
sev = f.get("severity")
|
||||
if sev == "CRITICAL":
|
||||
crit_count += 1
|
||||
elif sev == "HIGH":
|
||||
high_count += 1
|
||||
elif sev == "MEDIUM":
|
||||
med_count += 1
|
||||
|
||||
unprotected_backups = sum(
|
||||
1 for i in audited_instances if i["backup_count"] == 0
|
||||
)
|
||||
pzi_compliant = sum(1 for i in audited_instances if i["satisfies_pzi"])
|
||||
|
||||
if crit_count > 0:
|
||||
posture_grade = "Grade F (Critical Security Exposures Detected)"
|
||||
status_badge = "🔴 CRITICAL RISK"
|
||||
elif high_count > 0:
|
||||
posture_grade = "Grade C (Action Required - High Risk Findings)"
|
||||
status_badge = "🟠 ELEVATED RISK"
|
||||
elif med_count > 0:
|
||||
posture_grade = "Grade B (Moderate - Configuration Gaps Identified)"
|
||||
status_badge = "🟡 MODERATE"
|
||||
else:
|
||||
posture_grade = "Grade A (Healthy & Compliant)"
|
||||
status_badge = "🟢 HEALTHY"
|
||||
|
||||
if total_instances > 0:
|
||||
protected_ratio = (
|
||||
total_instances - unprotected_backups
|
||||
) / total_instances
|
||||
coverage_pct = round(protected_ratio * 100, 1)
|
||||
else:
|
||||
coverage_pct = 100.0
|
||||
|
||||
return {
|
||||
"project_id": self.project_id,
|
||||
"total_instances": total_instances,
|
||||
"posture_grade": posture_grade,
|
||||
"status_badge": status_badge,
|
||||
"metrics": {
|
||||
"critical_findings": crit_count,
|
||||
"high_findings": high_count,
|
||||
"medium_findings": med_count,
|
||||
"unprotected_instances": unprotected_backups,
|
||||
"pzi_compliant_count": pzi_compliant,
|
||||
"backup_coverage_pct": coverage_pct,
|
||||
},
|
||||
"instances": audited_instances,
|
||||
}
|
||||
|
||||
|
||||
def _render_scorecard(report: Dict[str, Any]) -> List[str]:
|
||||
"""Renders the Executive Posture Scorecard section."""
|
||||
lines: List[str] = [
|
||||
f"## Executive Posture Scorecard: `{report['project_id']}`",
|
||||
"",
|
||||
"| Metric | Status | Details |",
|
||||
"| :--- | :--- | :--- |",
|
||||
(
|
||||
f"| **Overall Health Posture** | **{report['status_badge']}** |"
|
||||
f" {report['posture_grade']} |"
|
||||
),
|
||||
(
|
||||
f"| **Instances Audited** | `{report['total_instances']}` | Total"
|
||||
" Filestore instances evaluated |"
|
||||
),
|
||||
]
|
||||
m = report["metrics"]
|
||||
lines.append(
|
||||
f"| **Backup Protection Rate** | **{m['backup_coverage_pct']}%** |"
|
||||
f" `{report['total_instances'] - m['unprotected_instances']}/{report['total_instances']}`"
|
||||
" instances have active backups |"
|
||||
)
|
||||
lines.append(
|
||||
f"| **Critical & High Security Findings** | `{m['critical_findings']}"
|
||||
f" Critical, {m['high_findings']} High` | Open exports, root squash, or"
|
||||
" missing backups |"
|
||||
)
|
||||
lines.append(
|
||||
"| **PZI Isolation Compliance** |"
|
||||
f" `{m['pzi_compliant_count']}/{report['total_instances']}` | Physical"
|
||||
" Zone Isolation adherence |"
|
||||
)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
||||
def _render_findings_matrix(report: Dict[str, Any]) -> List[str]:
|
||||
"""Renders the Priority Findings & Remediation Matrix section."""
|
||||
lines: List[str] = ["## Priority Findings & Remediation Matrix", ""]
|
||||
all_findings = []
|
||||
for inst in report["instances"]:
|
||||
for f in inst["findings"]:
|
||||
all_findings.append((inst["instance_id"], f))
|
||||
|
||||
if not all_findings:
|
||||
lines.append(
|
||||
"🎉 **No security, compliance, or backup risks detected! All instances"
|
||||
" are compliant.**"
|
||||
)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
sev_order = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}
|
||||
all_findings.sort(key=lambda x: sev_order.get(x[1]["severity"], 99))
|
||||
|
||||
lines.append(
|
||||
"| Severity | Instance ID | Category | Finding Description |"
|
||||
" Remediation Plan |"
|
||||
)
|
||||
lines.append("| :--- | :--- | :--- | :--- | :--- |")
|
||||
for inst_id, f in all_findings:
|
||||
sev_emoji = {
|
||||
"CRITICAL": "🚨 **CRITICAL**",
|
||||
"HIGH": "⚠️ **HIGH**",
|
||||
"MEDIUM": "ℹ️ **MEDIUM**",
|
||||
"LOW": "🟢 **LOW**",
|
||||
}.get(f["severity"], f["severity"])
|
||||
lines.append(
|
||||
f"| {sev_emoji} | `{inst_id}` | {f['category']} | {f['message']} |"
|
||||
f" {f['remediation']} |"
|
||||
)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
||||
def _render_inventory_table(report: Dict[str, Any]) -> List[str]:
|
||||
"""Renders the Filestore Instance Inventory & Compliance Status table."""
|
||||
lines: List[str] = [
|
||||
"## Filestore Instance Inventory & Compliance Status",
|
||||
"",
|
||||
(
|
||||
"| Instance ID | Location | Tier | Capacity | Reserved CIDR | Write"
|
||||
" IOPS | Throughput | PZI | PZS | Backups | Latest Backup |"
|
||||
),
|
||||
(
|
||||
"| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |"
|
||||
" :--- | :--- |"
|
||||
),
|
||||
]
|
||||
for i in report["instances"]:
|
||||
pzi_str = "✅ Yes" if i["satisfies_pzi"] else "❌ No"
|
||||
if i["tier"] in ["REGIONAL", "ENTERPRISE"]:
|
||||
pzs_str = "✅ Yes" if i["satisfies_pzs"] else "❌ No"
|
||||
else:
|
||||
pzs_str = "N/A"
|
||||
b_count_str = (
|
||||
"🔴 0" if i["backup_count"] == 0 else f"✅ {i['backup_count']}"
|
||||
)
|
||||
lines.append(
|
||||
f"| `{i['instance_id']}` | `{i['location']}` | `{i['tier']}` |"
|
||||
f" `{i['capacity_gb']} GiB` | `{i['reserved_ip_range']}` |"
|
||||
f" `{i['max_write_iops']}` | `{i['max_throughput']}` | {pzi_str} |"
|
||||
f" {pzs_str} | {b_count_str} | {i['latest_backup_date']} |"
|
||||
)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
||||
def _render_remediation_actions(report: Dict[str, Any]) -> List[str]:
|
||||
"""Renders Automated Remediation Actions (Disaster Recovery) section."""
|
||||
unprotected = [i for i in report["instances"] if i["backup_count"] == 0]
|
||||
if not unprotected:
|
||||
return []
|
||||
|
||||
lines: List[str] = [
|
||||
"## Automated Remediation Actions (Disaster Recovery)",
|
||||
"",
|
||||
(
|
||||
"To create baseline backups for unprotected instances, execute the"
|
||||
" following commands:"
|
||||
),
|
||||
"```bash",
|
||||
]
|
||||
for i in unprotected:
|
||||
loc_flag = (
|
||||
f"--instance-location={i['location']}"
|
||||
if i["is_regional"]
|
||||
else f"--instance-zone={i['location']}"
|
||||
)
|
||||
lines.append(
|
||||
f'CLOUDSDK_METRICS_ENVIRONMENT="{METRICS_ENV}" \\\n'
|
||||
f"gcloud filestore backups create {i['instance_id']}-backup-$(date"
|
||||
" +%Y%m%d) \\\n"
|
||||
f" --project={report['project_id']} \\\n"
|
||||
f" --instance={i['instance_id']} \\\n"
|
||||
f" --file-share={i['file_share']} \\\n"
|
||||
f" {loc_flag} \\\n"
|
||||
f" --region={i['backup_region']}"
|
||||
)
|
||||
lines.append("```")
|
||||
lines.append("")
|
||||
lines.append(
|
||||
"> **Confirmation Required**: Would you like me to execute these backup"
|
||||
" creation commands for the unprotected instances? Please confirm to"
|
||||
" proceed."
|
||||
)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
||||
def render_markdown_report(report: Dict[str, Any]) -> str:
|
||||
"""Renders the audit report in clean GitHub-flavored markdown."""
|
||||
lines: List[str] = [
|
||||
f"# Filestore Comprehensive Audit Report: `{report['project_id']}`",
|
||||
"",
|
||||
]
|
||||
lines.extend(_render_scorecard(report))
|
||||
lines.extend(_render_findings_matrix(report))
|
||||
lines.extend(_render_inventory_table(report))
|
||||
lines.extend(_render_remediation_actions(report))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Google Cloud Filestore Comprehensive Audit Engine."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--project", required=True, help="GCP Project ID to audit"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--instance", help="Specific Filestore Instance ID (optional)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--location", default="-", help="GCP zone or region (default: '-')"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--stale-backup-days",
|
||||
type=int,
|
||||
default=DEFAULT_STALE_BACKUP_DAYS,
|
||||
help=(
|
||||
"Threshold in days to flag stale backups"
|
||||
f" (default: {DEFAULT_STALE_BACKUP_DAYS})"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--format",
|
||||
choices=["markdown", "json"],
|
||||
default="markdown",
|
||||
help="Output format (markdown or json)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
auditor = FilestoreAuditor(
|
||||
project_id=args.project,
|
||||
instance_id=args.instance,
|
||||
location=args.location,
|
||||
stale_backup_days=args.stale_backup_days,
|
||||
)
|
||||
|
||||
try:
|
||||
report = auditor.run_audit()
|
||||
except (RuntimeError, ValueError, OSError, json.JSONDecodeError) as e:
|
||||
err_msg = str(e)
|
||||
if args.format == "json":
|
||||
print(json.dumps({"error": err_msg}))
|
||||
else:
|
||||
print(f"**Error executing audit:** {err_msg}")
|
||||
sys.exit(1)
|
||||
|
||||
if args.format == "json":
|
||||
print(json.dumps(report, indent=2))
|
||||
else:
|
||||
print(render_markdown_report(report))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user