Cloud Asset Inventory: Truy vấn trạng thái tài nguyên & tuân thủ
Vì sao cần Cloud Asset Inventory
Khi phân cấp mở rộng, rất khó để duy trì cái nhìn chính xác về toàn bộ resources:
Organization với 1000 projects, 100,000 resources:
- VM nào có external IP? (vi phạm tuân thủ nếu không mong đợi)
- Ai đang có role Owner? (kiểm toán bảo mật)
- Resource nào đang ở trạng thái soft-delete? (theo dõi quota)
- Resource Compute Engine nào không tuân thủ policy bảo mật?
- Trạng thái hiện tại của resource X là gì? (khắc phục sự cố)Cách làm thủ công không hiệu quả:
- Truy vấn từng service riêng lẻ (chậm, thiếu sót)
- Chạy lệnh
gcloud(không mở rộng tốt) - Phân tích audit logs (phản ứng sau sự việc, không chủ động)
Cloud Asset Inventory cung cấp:
- Một view thống nhất của toàn bộ resources (200+ loại resource)
- API có thể truy vấn với hỗ trợ filtering/biểu thức
- Trạng thái resource thời gian thực (live query) + lịch sử (qua export)
- Công cụ tuân thủ (Policy Analyzer, tích hợp Security Command Center)
Kiến trúc
Cloud Asset Inventory duy trì hai kho dữ liệu:
1. Metadata tài sản thời gian thực
Organization → Folder → Project → Resources
↓
Cloud Asset Inventory (cơ sở dữ liệu đã index)
↓
Query APIĐặc điểm:
- Trạng thái resource trực tiếp
- Có thể truy vấn trong vài giây
- Lưu trữ theo region (phụ thuộc vào vị trí asset)
- Có đầy đủ ngữ cảnh phân cấp
2. Export lịch sử
Dữ liệu thời gian thực → Export vào BigQuery / Cloud Storage
↓
Phân tích lịch sử
Phát hiện xu hướng
Kiểm toán tuân thủĐặc điểm:
- Lưu đầy đủ lịch sử
- Truy vấn được bằng BigQuery SQL
- Có chính sách retention tùy chỉnh
- Chi phí: Storage + BigQuery query
Mẫu truy vấn
Mẫu 1: Tìm tất cả resource theo loại
bash
# Tìm tất cả Compute Engine instances
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--asset-types=compute.googleapis.com/Instance
# Tìm tất cả Cloud Storage buckets
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--asset-types=storage.googleapis.com/Bucket
# Tìm nhiều loại cùng lúc
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--asset-types=compute.googleapis.com/Instance,storage.googleapis.com/BucketMẫu 2: Lọc theo thuộc tính resource
bash
# Tìm VM có external IP (rủi ro bảo mật tiềm năng)
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--asset-types=compute.googleapis.com/Instance \
--query="networkInterfaces.accessConfigs.natIP:*"
# Tìm resource trong project cụ thể
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--query="project:my-project-prod"
# Tìm resource theo location
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--query="location:us-central1"Mẫu 3: Kiểm toán tuân thủ
python
from google.cloud import asset_v1
def audit_external_ips(org_id):
"""Tìm tất cả VM có external IP không mong đợi"""
client = asset_v1.AssetServiceClient()
# Truy vấn VM có external IP
query = "networkInterfaces.accessConfigs.natIP:*"
request = asset_v1.SearchAllResourcesRequest(
scope=f"organizations/{org_id}",
asset_types=["compute.googleapis.com/Instance"],
query=query
)
findings = []
for result in client.search_all_resources(request=request):
# Kiểm tra external IP có được phép hay không
if not is_external_ip_allowed(result):
findings.append({
"resource": result.name,
"project": result.project,
"issue": "Unexpected external IP"
})
return findings
def audit_owner_role(org_id):
"""Tìm tất cả user/service account có role Owner"""
client = asset_v1.AssetServiceClient()
# Truy vấn IAM bindings
request = asset_v1.SearchAllIamPoliciesRequest(
scope=f"organizations/{org_id}",
query='roles/owner'
)
findings = []
for result in client.search_all_iam_policies(request=request):
for binding in result.bindings:
if binding.role == "roles/owner":
for member in binding.members:
findings.append({
"resource": result.resource,
"member": member,
"role": binding.role
})
return findingsPhát hiện drift
Cloud Asset Inventory cho phép kiểm tra tuân thủ tự động:
python
def detect_resource_drift(resource_id, expected_config):
"""Phát hiện resource có lệch khỏi trạng thái kỳ vọng hay không"""
from google.cloud import asset_v1
client = asset_v1.AssetServiceClient()
# Lấy trạng thái hiện tại của asset
response = client.list_assets(
parent=f"projects/PROJECT_ID",
asset_types=[resource_type]
)
actual_config = None
for asset in response:
if asset.name == resource_id:
actual_config = asset.resource
break
if not actual_config:
return {"status": "MISSING", "drift": expected_config}
# So sánh các field
drifts = {}
for key, expected_value in expected_config.items():
actual_value = actual_config.get(key)
if actual_value != expected_value:
drifts[key] = {
"expected": expected_value,
"actual": actual_value
}
if drifts:
return {"status": "DRIFTED", "drifts": drifts}
else:
return {"status": "COMPLIANT"}Kiểm toán IAM Policy
bash
# Tìm tất cả service account có role cụ thể
gcloud asset search-all-iam-policies \
--scope=organizations/ORG_ID \
--query='roles/serviceaccountUser'
# Tìm quyền của một member cụ thể
gcloud asset search-all-iam-policies \
--scope=organizations/ORG_ID \
--query='members:user@example.com'
# Export tất cả IAM policies sang BigQuery
gcloud asset export --project=EXPORT_PROJECT \
--bucket=gs://asset-export-bucket \
--asset-types=cloudresourcemanager.googleapis.com/ProjectPhân tích lịch sử qua BigQuery
sql
-- Truy vấn dữ liệu asset lịch sử
-- (sau khi export Cloud Asset Inventory sang BigQuery)
SELECT
TIMESTAMP(DATE(timestamp)) as date,
asset_type,
COUNT(*) as resource_count
FROM `project.dataset.cloudresourcemanager_assets_YYYYMM`
WHERE asset_type = 'compute.googleapis.com/Instance'
GROUP BY date, asset_type
ORDER BY date DESC;
-- Phát hiện resource mới tạo
SELECT
name,
display_name,
create_time
FROM `project.dataset.compute_instances_YYYYMM`
WHERE DATE(create_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
ORDER BY create_time DESC;
-- Tìm resource thiếu label bắt buộc
SELECT
name,
display_name,
STRUCT(
JSON_EXTRACT_SCALAR(resource.data, '$.labels.environment') as environment,
JSON_EXTRACT_SCALAR(resource.data, '$.labels.team') as team
) as labels
FROM `project.dataset.compute_instances_YYYYMM`
WHERE DATE(timestamp) = CURRENT_DATE()
AND (
JSON_EXTRACT_SCALAR(resource.data, '$.labels.environment') IS NULL
OR JSON_EXTRACT_SCALAR(resource.data, '$.labels.team') IS NULL
);Tích hợp Security Command Center
python
from google.cloud import securitycenter_v1
def create_compliance_finding(org_id, resource_name, issue):
"""Tạo finding Security Command Center từ kiểm tra tuân thủ"""
client = securitycenter_v1.SecurityCenterClient()
parent = f"organizations/{org_id}"
source_name = f"{parent}/sources/FINDING_SOURCE_ID"
finding = securitycenter_v1.Finding(
state=securitycenter_v1.Finding.State.ACTIVE,
resource_name=resource_name,
finding_class=securitycenter_v1.Finding.FindingClass.MISCONFIGURATION,
severity=securitycenter_v1.Finding.Severity.HIGH,
source_properties={
"issue": issue,
"recommendation": "Fix configuration"
}
)
client.create_finding(
parent=source_name,
finding=finding
)Cân nhắc về khả năng mở rộng
Truy vấn quy mô lớn
bash
# Với org lớn, truy vấn có thể mất thời gian
# Hãy dùng filter để thu hẹp phạm vi
# ❌ Quá rộng
gcloud asset search-all-resources \
--scope=organizations/ORG_ID
# ✅ Thu hẹp hơn
gcloud asset search-all-resources \
--scope=organizations/ORG_ID \
--asset-types=compute.googleapis.com/Instance \
--query="location:us-central1"Đổi trade-off giữa Export và Query
Dùng Real-time Query API khi:
- Cần trạng thái hiện tại
- Truy vấn một lần
- < 100k resources
Dùng BigQuery Exports khi:
- Cần phân tích lịch sử
- Truy vấn lặp lại (kinh tế hơn)
- Báo cáo tuân thủ quy mô lớn
- Phân tích xu hướngChi phí
Cloud Asset Inventory:
- Truy vấn thời gian thực: miễn phí (đã bao gồm)
- Export: $1 cho mỗi 1M resources mỗi tháng
- BigQuery: tính theo giá query chuẩn ($6.25 mỗi TB)
Ví dụ org với 100k resources:
- Export: $0.10/tháng
- Query: 10 queries × $0.01 mỗi query (ước tính) = $0.10/tháng
- Tổng: khoảng $0.20/tháng (rất rẻ)Giám sát qua Policy Analyzer
Policy Analyzer phát hiện cấu hình IAM sai:
python
def analyze_iam_policy(org_id):
"""Kiểm tra IAM policies quá rộng"""
from google.cloud import asset_v1
client = asset_v1.AssetServiceClient()
# Phân tích IAM policy
analysis = client.analyze_iam_policy(
analysis_query=asset_v1.IamPolicyAnalysisQuery(
scope=f"organizations/{org_id}",
resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
# Query tất cả resources
),
access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
permissions=["*"] # Tất cả permissions
),
identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
identity="principalSet://goog/public:all" # Public access
)
)
)
# Xem các finding về public access rủi ro
for result in analysis.analysis_results:
if result.fully_explored:
# Resource cho phép public access
print(f"⚠️ {result.resource} cho phép public access")Mẫu tự động hóa
python
# Kiểm tra tuân thủ liên tục
def schedule_compliance_audit():
"""Chạy kiểm tra tuân thủ theo lịch"""
from google.cloud import scheduler_v1
# Tạo Cloud Scheduler job
scheduler_client = scheduler_v1.CloudSchedulerClient()
parent = f"projects/PROJECT_ID/locations/us-central1"
job = scheduler_v1.Job(
name=f"{parent}/jobs/compliance-audit",
description="Daily compliance audit",
schedule="0 2 * * *", # 2 AM hằng ngày
time_zone="UTC",
http_target=scheduler_v1.HttpTarget(
uri="https://us-central1-PROJECT_ID.cloudfunctions.net/audit",
http_method=scheduler_v1.HttpMethod.POST
)
)
scheduler_client.create_job(parent=parent, job=job)
# Cloud Function cho audit tuân thủ
def compliance_audit(request):
"""Cloud Function được scheduler kích hoạt"""
findings = []
findings.extend(audit_external_ips(ORG_ID))
findings.extend(audit_owner_role(ORG_ID))
if findings:
notify_security_team(findings)
return {"status": "completed", "findings": len(findings)}