WAF-COST-090 โ€“ Data Transfer & Egress Cost Management

Description

VPC endpoints MUST be deployed for S3, KMS, and other heavily-used internal cloud services to eliminate unnecessary internet egress costs. Public IP addresses on internal compute resources (that do not serve public traffic) are prohibited. CDN MUST be configured for all publicly accessible static assets. An egress cost budget alert MUST exist. Cross-region data transfer MUST be minimized and documented when necessary.

Rationale

Risk Description

Risk

Internet-facing resources or open management ports become direct attack vectors.

Risk

Lateral movement is easier when network segmentation and access rules are missing.

Requirements

  • VPC endpoints MUST be deployed for S3, KMS, and other heavily-used internal cloud services to eliminate unnecessary internet egress costs.

  • CDN MUST be configured for all publicly accessible static assets.

  • An egress cost budget alert MUST exist.

  • Cross-region data transfer MUST be minimized and documented when necessary.

Implementation Guidance

  1. Document the requirement in a version-controlled policy or standard.

  2. Assign ownership and establish a review cadence (e.g., quarterly).

  3. Implement the required configuration as code in Terraform or equivalent IaC.

Maturity Levels

Level Name Criteria

L1

Ad hoc โ€“ control not consistently applied

* Manual evidence exists for some workloads, but coverage is unknown.

L2

Standardised โ€“ policy defined and communicated

* A version-controlled policy, ADR, or standard describes the requirement.

L3

Integrated โ€“ automated validation in CI/CD

* Terraform checks run automatically on pull requests and report results.

L4

Auditable โ€“ metrics and exceptions tracked

* Control coverage, failures, and approved exceptions are reported quarterly.

L5

Optimised โ€“ continuous improvement based on data

* Control effectiveness is measured and iteratively improved.

Checks

waf-cost-090.tf.aws.vpc-endpoint-s3-exists

Engine: terraform | Provider: aws | Automated: Yes

VPC endpoint for S3 must exist in each production VPC

An aws_vpc_endpoint resource with service_name matching S3 must exist for each production VPC. Without S3 VPC endpoints, all S3 traffic from EC2 instances flows through the internet gateway, generating unnecessary egress and data processing charges.

Scope: resource โ€” aws_vpc_endpoint

Assertions: * service_name matches pattern .*\\.s3$ โ€” VPC endpoint for S3 (service_name ending in .s3) must exist. * vpc_endpoint_type in ['Gateway', 'Interface'] โ€” VPC endpoint type must be Gateway or Interface.

Remediation: Add an aws_vpc_endpoint resource for S3 to each production VPC. For S3: use vpc_endpoint_type = "Gateway" (free of charge). For interface endpoints (KMS, ECR, STS): vpc_endpoint_type = "Interface" (hourly charge but saves on data processing and egress from NAT gateway).

Compliant
resource "aws_vpc_endpoint" "s3" {
  vpc_id            = aws_vpc.main.id
  service_name      = "com.amazonaws.${var.region}.s3"
  vpc_endpoint_type = "Gateway"
  route_table_ids   = [aws_route_table.private.id]

  tags = merge(module.mandatory_tags.tags, {
    Name = "vpce-s3-${var.environment}"
  })
}

resource "aws_vpc_endpoint" "kms" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.kms"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoint.id]
  private_dns_enabled = true

  tags = merge(module.mandatory_tags.tags, {
    Name = "vpce-kms-${var.environment}"
  })
}
Non-Compliant
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  # No VPC endpoints defined โ€“ all S3/KMS traffic via internet gateway
  # WAF-COST-090 violation: unnecessary egress charges
}

waf-cost-090.tf.aws.no-public-ip-internal-compute

Engine: terraform | Provider: aws | Automated: Yes

Internal compute resources must not have public IPs

EC2 instances used for internal application workloads (not serving public traffic directly) must not have associate_public_ip_address = true. Internal compute should use private IPs with NAT Gateway for outbound internet access where needed.

Scope: resource โ€” aws_instance, aws_launch_template

Assertions: * associate_public_ip_address is false โ€” Internal compute must not have public IP addresses. Use private subnets with NAT Gateway.

Remediation: Set associate_public_ip_address = false on internal EC2 instances. Place instances in private subnets. Use NAT Gateway for outbound internet access. Use ALB/NLB with public IPs for load balancing inbound public traffic.

Compliant
resource "aws_instance" "app" {
  ami                         = data.aws_ami.ubuntu.id
  instance_type               = "t3.medium"
  subnet_id                   = aws_subnet.private.id
  associate_public_ip_address = false  # Internal compute, no public IP
  tags                        = module.mandatory_tags.tags
}
Non-Compliant
resource "aws_instance" "app" {
  ami                         = data.aws_ami.ubuntu.id
  instance_type               = "t3.medium"
  subnet_id                   = aws_subnet.public.id
  associate_public_ip_address = true  # Unnecessary public IP for internal app server
}

waf-cost-090.tf.azurerm.private-endpoint-internal-services

Engine: terraform | Provider: azurerm | Automated: Yes

Azure private endpoints must be used for internal PaaS services

azurerm_private_endpoint resources must route traffic to internal Azure PaaS services (e.g., Storage, Key Vault, SQL) over the Azure backbone. Internal compute resources must not have public IPs.

Scope: resource โ€” azurerm_private_endpoint

Assertions: * private_service_connection attribute exists โ€” Azure private endpoint must define a private service connection.

Remediation: Create azurerm_private_endpoint resources for Storage accounts, Key Vaults, and SQL databases. Set public_network_access_enabled = false on those services where applicable. Place internal VMs in private subnets without public IPs.

Compliant
resource "azurerm_private_endpoint" "storage" {
  name                = "pe-storage"
  location            = var.location
  resource_group_name = azurerm_resource_group.main.name
  subnet_id           = azurerm_subnet.private.id

  private_service_connection {
    name                           = "psc-storage"
    private_connection_resource_id = azurerm_storage_account.data.id
    subresource_names              = ["blob"]
    is_manual_connection           = false
  }
}

resource "azurerm_storage_account" "data" {
  name                     = "acmeappdata"
  resource_group_name      = azurerm_resource_group.main.name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  public_network_access_enabled = false
}
Non-Compliant
resource "azurerm_storage_account" "data" {
  name                     = "acmeappdata"
  resource_group_name      = azurerm_resource_group.main.name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  public_network_access_enabled = true  # Traffic via public internet
}

waf-cost-090.tf.google.private-google-access

Engine: terraform | Provider: google | Automated: Yes

GCP internal VMs must use Private Google Access and VPC-SC

GCP subnets must have private_ip_google_access enabled so internal VMs reach Google APIs without public IPs. google_access_context_manager_service_perimeter resources should restrict access to sensitive APIs.

Scope: resource โ€” google_compute_subnetwork

Assertions: * private_ip_google_access is true โ€” GCP private subnet must enable Private Google Access to avoid public IP egress.

Remediation: Enable private_ip_google_access on private subnets. Configure VPC Service Controls perimeters for Cloud Storage, BigQuery, and KMS. Avoid public IPs on internal compute.

Compliant
resource "google_compute_subnetwork" "private" {
  name          = "private-subnet"
  ip_cidr_range = "10.0.1.0/24"
  region        = var.region
  network       = google_compute_network.main.id

  private_ip_google_access = true
}
Non-Compliant
resource "google_compute_subnetwork" "private" {
  name          = "private-subnet"
  ip_cidr_range = "10.0.1.0/24"
  region        = var.region
  network       = google_compute_network.main.id
  private_ip_google_access = false  # Internal VMs need public IPs for Google APIs
}

waf-cost-090.tf.stackit.private-network-services

Engine: terraform | Provider: stackit | Automated: Yes

STACKIT internal services must be reachable via private network

STACKIT internal services must be reachable via private network. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: resource โ€” stackit_network, stackit_object_storage_bucket, stackit_postgres_instance, stackit_mysql_instance, stackit_redis_instance, stackit_opensearch_instance

Assertions: * network_id attribute exists โ€” STACKIT managed service should be attached to a private network.

Remediation: Review the Terraform configuration for the listed resource types and ensure the required attribute or tag is present. See the control narrative for provider-specific guidance.

Compliant
# Example resource meets the requirement.
resource "stackit_network" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "stackit_network" "example" {
  # Missing required attribute/tag/label
}

waf-cost-090.tf.ovh.private-network-services

Engine: terraform | Provider: ovh | Automated: Yes

OVHcloud internal services must be reachable via private network

OVHcloud internal services must be reachable via private network. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: resource โ€” ovh_cloud_project_network_private, ovh_cloud_project_instance, ovh_cloud_project_kube, ovh_cloud_project_database_postgresql, ovh_cloud_project_database_mysql

Assertions: * network_id attribute exists โ€” OVHcloud internal service should reference a private network.

Remediation: Review the Terraform configuration for the listed resource types and ensure the required attribute or tag is present. See the control narrative for provider-specific guidance.

Compliant
# Example resource meets the requirement.
resource "ovh_cloud_project_network_private" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "ovh_cloud_project_network_private" "example" {
  # Missing required attribute/tag/label
}

waf-cost-090.tf.hcloud.private-network-services

Engine: terraform | Provider: hcloud | Automated: Yes

Hetzner Cloud internal services must be reachable via private network

Hetzner Cloud internal services must be reachable via private network. This check validates the Terraform configuration uses the expected resource and attribute.

Scope: resource โ€” hcloud_network, hcloud_server, hcloud_load_balancer, hcloud_volume

Assertions: * network_id attribute exists โ€” Hetzner Cloud internal service should be attached to a private network.

Remediation: Review the Terraform configuration for the listed resource types and ensure the required attribute or tag is present. See the control narrative for provider-specific guidance.

Compliant
# Example resource meets the requirement.
resource "hcloud_network" "example" {
  # required attribute/tag/label present
}
Non-Compliant
# Example resource does not meet the requirement.
resource "hcloud_network" "example" {
  # Missing required attribute/tag/label
}

Evidence

Kind Required Description

iac

Required

Terraform configuration implementing the control.

governance

Required

Policy, ADR, or standard documenting the requirement.

config

Optional

Cloud console or API output confirming runtime state.

Framework Controls

FinOps Foundation

* Optimize Phase โ€“ Network Cost Optimization * Optimize Phase โ€“ Data Transfer Cost Reduction