Network Tags vs Service Accounts — Two Ways to Target Firewall Rules
Executive Summary
Firewall rules phải specify target (VMs nhận traffic) và có thể filter theo source.
Hai cơ chế targeting:
- ✅ Network Tags: Label cho VMs, dễ quản lý
- ✅ Service Accounts: IAM-integrated, enforce access control
- ❌ Không thể mix cả hai trong một rule (phải chọn một)
Hiểu trade-offs giúp design scalable firewall policies.
Network Tags: Simple Labeling
What Are Network Tags?
Network tags = metadata labels gán cho VMs, không liên quan IAM:
bash
gcloud compute instances create web-server \
--tags=http-server,production,tier-1
# VM tags:
# - http-server (firewall targeting)
# - production (operational tracking)
# - tier-1 (architecture tier)Targeting with Tags
Firewall rule: allow-http
Properties:
Direction: INGRESS
Target Tags: [http-server] ← Apply này rule đến VMs with tag "http-server"
Source Ranges: 0.0.0.0/0
Protocol: tcp:80
Action: ALLOW
Effect:
VM "web-1" (tags: http-server, production)
→ Has tag "http-server"? YES
→ Rule applies ✓
VM "db-1" (tags: database, production)
→ Has tag "http-server"? NO
→ Rule does NOT apply ✗Tag Management
bash
# Add tag to running VM:
gcloud compute instances add-tags web-1 \
--tags=monitoring-enabled
# Remove tag:
gcloud compute instances remove-tags web-1 \
--tags=monitoring-enabled
# Update VM startup script to tag itself:
(Not possible - tags are GCP-managed, not self-service from VM)
# List VMs by tag:
gcloud compute instances list --filter="tags:http-server"
# Check VM tags:
gcloud compute instances describe web-1 \
--format="value(tags.items)"Tag Limitations
❌ Case-sensitive (tag "http" ≠ tag "HTTP") ❌ Alphanumeric + hyphens only (no underscores, dots) ❌ Max 64 tags per VM ❌ Cannot use wildcards in rules (exact match only) ❌ No tag hierarchy (flat namespace)
bash
# Invalid tag names:
--tags=http_server ← Underscore not allowed
--tags=http.server ← Dot not allowed
# Valid:
--tags=http-server,web-tier-1Tag Conflicts: Same Tag, Different VMs
Production misconfig:
VM "web-1" (tags: loadbalancer, production)
VM "db-1" (tags: loadbalancer, production)
Firewall rule:
Target Tags: [loadbalancer]
Action: ALLOW tcp:443
Effect: BOTH web-1 AND db-1 receive traffic on 443
Database exposed to internet (security breach!)
Root cause: Tag reuse across tiers
Prevention:
Use hierarchical naming:
- tag:tier1-lb
- tag:tier2-app
- tag:tier3-db
Then each rule targets specific tierService Accounts: IAM-Integrated Targeting
What Are Service Accounts?
Service accounts = principals in IAM, used for:
- VMs to authenticate to Google APIs
- Firewall rule targeting (bonus feature)
bash
gcloud iam service-accounts create backend-app-sa \
--display-name="Backend App Service Account"
# Output: backend-app-sa@PROJECT.iam.gserviceaccount.com
# Assign to VM:
gcloud compute instances create app-1 \
--service-account=backend-app-sa@PROJECT.iam.gserviceaccount.comTargeting with Service Accounts
Firewall rule: allow-from-backend
Properties:
Direction: INGRESS
Source Service Accounts: [backend-app-sa@PROJECT.iam.gserviceaccount.com]
Target Tags: [database] ← Still need target (tags OR SAs, not SAs alone)
Protocol: tcp:3306
Action: ALLOW
Effect:
Packet from app-1 (SA: backend-app-sa) to db-1 (tag: database)
→ Source SA matches backend-app-sa? YES ✓
→ Target has tag "database"? YES ✓
→ Rule applies ✓
Packet from web-1 (SA: frontend-sa) to db-1
→ Source SA matches backend-app-sa? NO ✗
→ Rule DOES NOT apply ✗Service Account vs Network Tags
yaml
Network Tags:
- What: Metadata labels
- Management: VMs own tags, operator assigns
- Enforcement: No IAM
- Mutability: Can change anytime (pod restart)
- Use case: Operational grouping
Service Accounts:
- What: IAM principals
- Management: Centralized, IAM-enforced
- Enforcement: IAM role restrictions
- Mutability: Only via IAM (managed centrally)
- Use case: Identity-based access controlTargeting Rules: Complete Reference
Rule A: Tags Only
bash
gcloud compute firewall-rules create allow-internal-tags \
--direction=INGRESS \
--priority=1000 \
--action=ALLOW \
--source-ranges=10.0.0.0/8 \
--target-tags=internal-service \
--allow=tcp:8080
# Target: VMs with tag "internal-service"
# Source filter: 10.0.0.0/8 onlyRule B: Service Accounts Only
bash
gcloud compute firewall-rules create allow-from-sa \
--direction=INGRESS \
--priority=1000 \
--action=ALLOW \
--source-service-accounts=backend-sa@project.iam.gserviceaccount.com \
--target-tags=database \
--allow=tcp:3306
# Target: VMs with tag "database"
# Source filter: must have backend-sa service accountRule C: Multiple Source Service Accounts
bash
gcloud compute firewall-rules create allow-multi-sa \
--direction=INGRESS \
--priority=1000 \
--action=ALLOW \
--source-service-accounts=backend-sa@project.iam.gserviceaccount.com,batch-sa@project.iam.gserviceaccount.com \
--target-tags=shared-storage \
--allow=tcp:445
# Multiple SAs (comma-separated)Rule D: Source Tags (Internal Communication)
bash
gcloud compute firewall-rules create allow-app-to-cache \
--direction=INGRESS \
--priority=1000 \
--action=ALLOW \
--source-tags=app-tier \
--target-tags=cache-tier \
--allow=tcp:6379
# Source: VMs with tag "app-tier"
# Target: VMs with tag "cache-tier"
# Internal traffic (no external IPs)Firewall Targeting Patterns
Pattern 1: Multi-Tier Architecture
Internet → Load Balancer (tier1-lb)
↓ [firewall: tcp:443 from internet]
Application Tier (tier2-app)
↓ [firewall: tcp:3306 from tier2-app]
Database Tier (tier3-database)
Firewall rules:
rule-1000:
source-ranges: 0.0.0.0/0
target-tags: tier1-lb
allow: tcp:443
rule-1100:
source-tags: tier1-lb
target-tags: tier2-app
allow: tcp:8080
rule-1200:
source-tags: tier2-app
target-tags: tier3-database
allow: tcp:3306
rule-60000:
action: DENY
(implicit deny-all catchall)Pattern 2: Cross-Project Service Accounts
Project A: Backend service
Service Account: backend-sa@project-a.iam.gserviceaccount.com
Project B: Database VM
VPC: prod-vpc (project-b)
VM: db-1 (tag: database)
Shared VPC setup: project-b hosts network, project-a accesses
Firewall rule (in project-b):
source-service-accounts: backend-sa@project-a.iam.gserviceaccount.com
target-tags: database
allow: tcp:5432
Effect: Only VMs from project-a with backend-sa can access dbPattern 3: GKE Pod Service Accounts
GKE cluster: prod-gke (namespace: production)
Kubernetes SA: payment-processor
Google SA: payment-processor@project.iam.gserviceaccount.com
Pod gets SA via Workload Identity:
Pod SA: payment-processor (Kubernetes)
Google SA: payment-processor@project.iam.gserviceaccount.com (GCP)
Firewall rule:
source-service-accounts: payment-processor@project.iam.gserviceaccount.com
target-tags: payment-db
allow: tcp:3306
Effect: Only payment-processor pods can reach payment database
Other pods cannot (even if in same namespace)Operational Considerations
Tag vs SA: Decision Matrix
Use TAGS when:
- Simple operational grouping (web, app, db)
- Teams change infrastructure frequently
- No need for centralized IAM enforcement
- Quick prototyping
Use SERVICE ACCOUNTS when:
- Production multi-team environments
- Need centralized identity enforcement
- Workload authentication required (to Google APIs)
- Compliance/audit trails (SA changes logged in IAM)
Use BOTH when:
- Target via SA (identity-based)
- AND filter additional conditions via tags
Example:
source-service-accounts: backend-sa
target-tags: production-database (not staging or test)Avoiding Tag Explosion
❌ Bad: Unlimited tag naming
--tags=prod,production,prod-env,prod-region-us,tier-2-app✅ Good: Structured naming
--tags=env-prod,tier-app,region-us-central1Debugging: Which Rule Applied?
bash
# Query firewall rules targeting a tag:
gcloud compute firewall-rules list \
--filter="targetTags:app-server" \
--format=table
# Query rules with specific source service account:
gcloud compute firewall-rules list \
--filter="sourceServiceAccounts:backend-sa*" \
--format=table
# Check VM tags:
gcloud compute instances describe app-1 \
--format="value(tags.items)"
# Check VM service account:
gcloud compute instances describe app-1 \
--format="value(serviceAccounts[0].email)"Best Practices
✅ Do:
- Use hierarchical tag names (tier-app, env-prod)
- Audit firewall rules monthly
- Document tag taxonomy across team
- Use SAs for production (IAM-enforced)
- Test rules before production deployment
❌ Don't:
- Mix tags + SAs in single rule (not supported)
- Reuse tags across incompatible tiers (db tagged "server")
- Create too many tags (limit namespace)
- Forget that tags can change without notice
- Assume tag targeting is secure (tags are metadata)
Conclusion
- Network Tags: Simple, operational, no enforcement
- Service Accounts: Identity-based, IAM-integrated, production-ready
- Best practice: Use SAs for security-critical rules, tags for operational grouping
For multi-tenant production: service accounts provide centralized control and audit trails.