Phạm vi Service Account & truy cập liên project
Nền tảng về Service Account
Service accounts là machine identity dùng để xác thực với GCP APIs:
User → danh tính người dùng (email@company.com)
Machine → service account (@project.iam.gserviceaccount.com)Hai cách xác thực:
- Keys (dài hạn, cách cũ)
- Tokens (ngắn hạn, khuyến nghị)
Keys vs Tokens
Keys (Mẫu cũ):
- File JSON chứa private key
- Dài hạn (cần xoay vòng thủ công)
- Lưu trên VM/laptop (rủi ro bị đánh cắp)
- Vẫn dùng cho:
- Phát triển cục bộ
- Công cụ bên ngoài
- Hệ thống cũ
Tokens (Mẫu mới):
- Tự động sinh
- Ngắn hạn (mặc định 1 giờ)
- Tự động xoay vòng
- Được tạo bởi:
- gcloud CLI
- Workload Identity (GKE/Cloud Run)
- Service account impersonationGiới hạn phạm vi của Service Account
Service account nên có quyền tối thiểu cần thiết:
python
# Ví dụ: service account cho batch processor
# Nó cần:
# 1. Đọc từ Cloud Storage (input data)
# 2. Ghi vào BigQuery (kết quả)
# 3. Ghi log
# ❌ TỆ: role Owner (quá rộng)
grant_role(service_account, "roles/owner")
# ✓ TỐT: custom role với quyền tối thiểu
custom_role_permissions = [
"storage.buckets.get",
"storage.objects.get",
"bigquery.datasets.get",
"bigquery.tables.get",
"bigquery.tables.update",
"bigquery.tables.updateData",
"logging.logEntries.create"
]
grant_custom_role(service_account, custom_role_permissions)Truy cập Service Account liên project
Mẫu 1: Truy cập project trực tiếp
Service Project A → Cấp quyền cho SA ← Host Project
(nơi SA được tạo) (nơi dùng resource)bash
# Tạo service account ở project A
gcloud iam service-accounts create batch-processor \
--project=PROJECT_A
# Cấp role ở project B (để dùng resource)
gcloud projects add-iam-policy-binding PROJECT_B \
--member=serviceAccount:batch-processor@PROJECT_A.iam.gserviceaccount.com \
--role=roles/bigquery.dataEditorMẫu 2: Chuỗi impersonation
Service account A impersonate B → C (lồng nhau):
bash
# Cấu trúc service account
# VM chạy bằng: vm-sa@project-a
# Cần truy cập: resources ở project-b
# Bước 1: vm-sa@project-a có quyền impersonate batch-sa@project-b
gcloud iam service-accounts add-iam-policy-binding \
batch-sa@project-b.iam.gserviceaccount.com \
--role=roles/iam.serviceAccountTokenCreator \
--member=serviceAccount:vm-sa@project-a.iam.gserviceaccount.com
# Bước 2: Code trên VM
from google.auth import impersonated_credentials
credentials = impersonated_credentials.Credentials(
source_credentials=compute.Credentials(),
target_principal="batch-sa@project-b.iam.gserviceaccount.com",
target_scopes=["https://www.googleapis.com/auth/cloud-platform"]
)
# Bước 3: Dùng credentials để truy cập resource project B
bigquery_client = bigquery.Client(
project="project-b",
credentials=credentials
)Workload Identity (khuyến nghị)
Phương pháp hiện đại: loại bỏ hoàn toàn service account keys:
yaml
# GKE Pod với Workload Identity
# 1. Kubernetes ServiceAccount (cấp k8s)
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: default
# 2. Gắn với GCP service account
# Pod chạy dưới dạng: app-sa (k8s) → app-gcp@project.iam.gserviceaccount.com (GCP)
# Trong code:
from google.cloud import bigquery
from google.auth import default
# Tự động dùng Workload Identity token
credentials, project = default()
client = bigquery.Client(
project=project,
credentials=credentials
)Ưu điểm của Workload Identity:
- Không có key để bị đánh cắp
- Tự động xoay vòng token
- Kiểm soát truy cập ở cấp pod
- Hoạt động với GKE, Cloud Run, Cloud Functions
Truy cập liên organization (trường hợp nâng cao)
Tình huống hiếm: Service account ở org A cần truy cập org B:
bash
# Cần trust rõ ràng giữa hai org
# Org A (nguồn):
# Service account: batch@proj-a.iam.gserviceaccount.com
# Org B (đích):
# 1. Tạo external identity pool (cho phép danh tính từ Org A)
# 2. Map service account của org A sang service account của org B
# 3. Cấp quyền
# Rất phức tạp, thường không cần. Tốt hơn là:
# - Giữ resource trong cùng organization
# - Dùng API/webhook cho giao tiếp giữa các orgBảo mật service account keys
Nếu bắt buộc phải dùng key (dev cục bộ, hệ thống cũ):
bash
# Tạo và lưu ngay
gcloud iam service-accounts keys create key.json \
--iam-account=dev-sa@project-id.iam.gserviceaccount.com
# Ngay lập tức siết quyền file
chmod 400 key.json
# Lưu ở nơi an toàn (không bao giờ commit Git!)
# Dùng environment variable
export GOOGLE_APPLICATION_CREDENTIALS=/secure/key.json
# Xoay vòng key thường xuyên
gcloud iam service-accounts keys list \
--iam-account=dev-sa@project-id.iam.gserviceaccount.com
# Xóa key cũ
gcloud iam service-accounts keys delete KEY_ID \
--iam-account=dev-sa@project-id.iam.gserviceaccount.comAudit trail cho impersonation
python
def audit_service_account_usage():
"""Ghi nhận ai đang dùng service account"""
from google.cloud import logging as cloud_logging
client = cloud_logging.Client()
# Truy vấn audit logs cho việc dùng service account
query = """
protoPayload.authenticationInfo.principalEmail ~ ".*@iam.gserviceaccount.com"
AND protoPayload.methodName ~ ".*iam.serviceaccounts.implicitDelegationCheck"
"""
entries = client.list_entries(filter_=query)
for entry in entries:
principal = entry.payload.get('authenticationInfo', {}).get('principalEmail')
method = entry.payload.get('methodName')
print(f"{principal} performed {method}")Quản lý Service Account bằng Terraform
hcl
# Tạo service account
resource "google_service_account" "batch" {
project = var.project_id
account_id = "batch-processor"
display_name = "Batch Processing Service Account"
}
# Cấp quyền ở project khác
resource "google_project_iam_member" "batch_bigquery_access" {
project = var.data_project_id
role = "roles/bigquery.dataEditor"
member = "serviceAccount:${google_service_account.batch.email}"
}
# Tạo key (chỉ khi thật sự cần)
resource "google_service_account_key" "batch_key" {
service_account_id = google_service_account.batch.name
public_key_type = "TYPE_X509_PEM_FILE"
}
# Lưu key an toàn (không bao giờ trong version control!)
output "service_account_key" {
value = google_service_account_key.batch_key.private_key
sensitive = true
}Theo dõi chi phí theo Service Account
sql
-- BigQuery: Theo dõi API calls theo service account
SELECT
protoPayload.authenticationInfo.principalEmail as service_account,
protoPayload.methodName as operation,
COUNT(*) as count,
DATE(timestamp) as date
FROM `project.dataset.cloudaudit_googleapis_com_activity`
WHERE DATE(timestamp) >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)
GROUP BY service_account, operation, date
ORDER BY count DESC;Anti-pattern
| Anti-pattern | Vấn đề | Giải pháp |
|---|---|---|
| Service account keys trên laptop | Key dễ bị đánh cắp | Dùng gcloud CLI / Workload Identity |
| Gán Owner cho service account | Làm được mọi thứ | Custom roles ít quyền nhất |
| Dùng cùng một SA cho mọi service | Không audit được theo service | Tạo SA riêng cho từng service |
| Keys sống lâu | Cửa sổ bị xâm phạm lớn | Dùng token ngắn hạn |
| Không xoay key | Key cũ vẫn hợp lệ nếu bị lộ | Tự động xoay vòng key |