Skip to content

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-1

Tag 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 tier

Service 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.com

Targeting 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 control

Targeting 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 only

Rule 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 account

Rule 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 db

Pattern 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-central1

Debugging: 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.