Ensure that VPC flow logs is enabled for every subnet in a VPC network

GCP
GCP

A VPC subnet with flow logs off produces no record of the TCP and UDP connections its VMs make, so an investigation into traffic that has already happened has nothing to read. The control requires flow logs to be on for each subnet whose purpose is PRIVATE, with a 5 second aggregation interval, a sampling rate of 1, all metadata included, and no filter applied.

What the benchmark asks for

The check reads four settings on every subnet, not just the on/off flag. Aggregation interval must be INTERVAL_5_SEC, flow sampling must be 1, metadata must be INCLUDE_ALL_METADATA, and no filterExpr may be set. The rate decides what fraction of aggregated flow entries are written, and the default is 0.5 when logging is turned on through the Compute Engine API and 1.0 through the Network Management API. A subnet enabled through the Compute Engine API and left at its default therefore sits below the 1 the check requires.

The purpose field decides whether a subnet is in scope. Subnets created for proxy use are not, and the benchmark excludes them.

How it works

Flow logging samples twice, and the two stages are not the same thing. Primary sampling takes packets at the host, and its rate is not yours to set. Google varies it with the load on the physical host, and a connection carrying more traffic is more likely to be sampled than a quiet one. Secondary sampling then aggregates the sampled packets into flow entries over the aggregation interval and applies the flow sampling rate you set, which is the 1 the control asks for and runs from 1.0 down to 0.0.

Setting flow sampling to 1 therefore means every aggregated entry is written. It does not mean every packet on the wire was seen. A short connection on a busy host can be missed by the primary sample and never reach the aggregation stage at all, so flow logs are evidence of what happened rather than a complete ledger.

Aggregation interval controls how much time one entry covers. At INTERVAL_5_SEC a long connection produces one entry for each five second interval in which its packets were sampled. Longer intervals collapse the same traffic into fewer entries and cost less to store.

Billing follows the network, not the workload. Log entries are charged to the project of the VPC network that reports the flow. On a Shared VPC, that is the host project, even when the VM generating the traffic lives in a service project. The team that owns the host project pays for logging decisions made by teams that do not.

Two APIs can turn flow logs on, and they behave differently. The Compute Engine API sets enableFlowLogs and logConfig on the subnet resource. The Network Management API, which Google now recommends, does not set those fields. A subnet configured through the newer API has flow logs running while the subnet resource still reports them as off, so a scanner that reads the subnet resource records a false negative. The benchmark's audit command reads exactly those fields.

The two APIs also do not share state. A configuration created through the Compute Engine API cannot be managed through the Network Management API's management surface, so moving a configuration from one to the other means creating it again rather than switching tools.

A subnet can also carry more than one configuration at once, for example one attached to the subnet and one inherited from the VPC network or the organization. Where their scopes overlap, the result is duplicate entries for the same traffic. Nothing merges them and nothing reports an error, so the first sign is a log volume higher than the traffic explains.

What the control does not cover

Flow logs cannot be enabled on a subnet whose purpose is INTERNAL_HTTPS_LOAD_BALANCER. Those are proxy-only subnets with no VM instances or serverless endpoints to sample traffic from, so there is nothing to log. A failing result against such a subnet is a scope error in the scanner, not a finding.

Coverage is per subnet, and a VM does not inherit it. A VM with network interfaces in several subnets of a VPC is only logged on the interfaces whose subnets have flow logs on. Turn logging on for one subnet and the traffic on that VM's other interfaces stays unrecorded. A subnet-by-subnet audit that reports 90 percent compliance says nothing about how many VMs are fully covered.

Retention is the other limit. Flow logs land in Cloud Logging and persist for 30 days by default. Meeting the control therefore gives you a month of network evidence, which an investigation that opens two months after an intrusion arrives too late to use, and a compliance program that expects a year falls short of. Anything longer than the default is a separate change. Set a custom retention period on the log bucket, or export the entries to BigQuery, Cloud Storage, or Pub/Sub.

What it costs to comply

The settings the control requires are the most expensive combination available. Sampling at 1 keeps every aggregated entry, a 5 second interval produces the largest number of entries per connection, and INCLUDE_ALL_METADATA makes each entry larger. The volume scales with how much the subnet's VMs talk, not with how many VMs there are, so a subnet carrying a chatty service mesh or a data pipeline generates far more than its instance count suggests.

The cost lands as Cloud Logging ingestion, plus storage in any export destination.

Three cases where the full configuration is not worth it. A subnet that only runs batch workloads moving bulk data between regions produces enormous flow volume and little security signal, and a longer aggregation interval gives the same picture for less, at the price of no longer meeting the control's INTERVAL_5_SEC requirement. A development subnet rebuilt weekly has no forensic value past its own lifetime. A subnet already covered by a network-level or organization-level flow log configuration gains nothing from a second configuration at the subnet level and pays twice for the overlap.

Enabling flow logs changes no traffic path and drops no packets, so there is no outage risk and nobody needs to be told before the change. The thing to tell people about is the bill, and the right audience is whoever owns the project that pays.

How to check your environment

The benchmark's audit reads the subnet resource directly. That is still the right place to look for configurations created through the Compute Engine API:

gcloud compute networks subnets list --format json

Each subnet in the output carries purpose, enableFlowLogs, and a logConfig block holding aggregationInterval, flowSampling, metadata, and filterExpr where they are set. A subnet with purpose of PRIVATE and no logConfig is the plain failure case.

That reading is incomplete on its own, because it misses anything configured through the Network Management API and anything inherited. To see every configuration affecting a subnet, including the subnet's own, its network's, and its organization's, query the Network Management API rather than the subnet resource. Run both and compare. A subnet that the Compute Engine API reports as off and the Network Management API reports as covered is already compliant in substance, and a subnet that both report as covered may be paying for duplicate entries.

To look at a single subnet:

gcloud compute networks subnets describe SUBNET_NAME --region=REGION

How to implement it

Enable flow logs on an existing subnet with every setting the control requires:

gcloud compute networks subnets update [SUBNET_NAME] --region [REGION] --enable-flow-logs --logging-aggregation-interval=interval-5-sec --logging-flow-sampling=1 --logging-metadata=include-all

The command is a subnet update, not a replacement, and running VMs keep running. Logging starts for new flows shortly after the update completes.

Before enabling, check whether a configuration already covers the subnet from the network or organization level. Adding a subnet-level configuration on top of one that already matches the same traffic produces duplicate entries, and the duplicates are billed.

Flow logs default to off when a subnet is created, so every subnet added after a remediation sweep fails again unless creation is constrained. gcloud compute networks subnets create takes the same four logging flags, so the simplest fix is to put them in whatever creates subnets. The Terraform module, the Deployment Manager template, or the script. A platform team that owns the module makes the setting the default for everyone who uses it.

Where a single configuration should cover many subnets, attach it at the network or organization level through the Network Management API instead of repeating it per subnet. One configuration covering a network stays correct as subnets are added, and it removes the gap between remediation and the next subnet someone creates. Keep in mind that tooling reading enableFlowLogs on the subnet will not see it, so the compliance scanner and the actual configuration need to agree on where to look before you take this route.

Set retention deliberately once logging is on. Cloud Logging keeps the entries for 30 days by default. Decide the retention period from how long an investigation in your environment realistically takes, then set a custom retention period on the log bucket or export the entries to match.

Further reading

  • VPC Flow Logs overview. The two sampling stages, what each field in a log record contains, multi-interface VM behavior, billing attribution, and retention defaults.
  • Use VPC Flow Logs. Enabling logging through the Network Management API, how configurations at different scopes combine, and the differences between the two APIs.

Found it. Fixed it. Done.

Start free. Understand your cloud in under an hour.