Business Challenge
Nine posts of this block have been about getting a packet to a destination. This one starts the half about whether it is allowed to arrive. The structure of a rule is simple enough to state in a sentence: a direction, a priority, an action, an enforcement status, a target, source and destination filters, and protocols and ports. What is worth a post is that only one of those orders rules against each other.
Priority does all the work: only the highest priority (lowest priority number) rule whose other components match traffic is applied; conflicting rules with lower priorities are ignored. And the documentation then removes the intuition most people bring to firewalls, twice over. Target specificity does not matter. Protocol and port specificity does not matter either. A rule written broadly can take precedence over the narrow rule you wrote to constrain it, purely on the number in the priority field — which is the same lesson #69 taught about routes, arriving one subsystem later.
It will not, unless its priority says so. The documentation gives both halves explicitly. On targets: the highest priority rule applicable to a target for a given type of traffic takes precedence. Target specificity does not matter. On ports: the highest priority rule applicable for a given protocol and destination port definition takes precedence, even when the protocol and destination port definition is more general — the worked example being a higher-priority allow for all protocols overriding a lower-priority deny of TCP 22 on the same targets.
Correct approachTreat the priority number as the entire ordering and the rest of the rule as a filter on what it applies to. Then write the broad deny at a low priority number only if you intend it to be unopenable, and keep selective allows above it. Reviewing a rule set means sorting by priority, not reading it for the narrowest match.
Only when the actions differ. A rule with a deny action overrides another with an allow action only if the two rules have the same priority — so equal priority with opposite actions is well defined. Equal priority with the same action is not: rules with the same priority and the same action have the same result. However, the rule that is used during the evaluation is indeterminate.
Correct approachThe traffic outcome is safe, so this is not a security bug — it is an attribution bug, and the documentation says where it bites: if you want your logs to show firewall rules being evaluated in a consistent and well-defined order, assign them unique priorities. If you rely on firewall logs to tell you which rule permitted something, duplicate priorities make that answer arbitrary.
For whole packets, yes. For fragments, the rule reaches less far than it reads: VPC firewall rules do not reassemble fragmented TCP packets, and therefore a firewall rule applicable to the TCP protocol can only apply to the first fragment because it contains the TCP header. The consequence is stated plainly: firewall rules applicable to the TCP protocol do not apply to the subsequent TCP fragments.
Correct approachKnow that port-based TCP filtering is a first-fragment control, and that a deny rule written against TCP ports does not describe what happens to later fragments of the same datagram. Where that matters, the filtering belongs somewhere that does reassemble — a proxy or an application load balancer — rather than in a rule whose documented scope is the header-bearing fragment.
Architecture
Each component is single-valued, and the singularity is the design. A rule is not a policy you compose; it is one assertion, and you build policy out of several at different priorities.
| Component | What it holds | What it commits you to |
|---|---|---|
| Direction | A direction from the perspective of the target; direction can be either ingress or egress | Each firewall rule applies to incoming (ingress) or outgoing (egress) connections, not both. Omitted, it defaults: if you don't specify a direction, Google Cloud uses ingress |
| Priority | Integer from 0 to 65535 , inclusive; default 1000 | The whole ordering. Lower number is higher priority |
| Action | An action on match , either allow or deny | Each firewall rule's action is either allow or deny — never conditional on anything else |
| Enforcement | enabled (default) or disabled | A rule can be parked rather than deleted — you can disable a rule for troubleshooting purposes |
| Target | Ingress: specifies the instances that receive packets. Egress: specifies the instances that send packets | Which instances the rule is about. #72 takes how you name them |
| Address family | IPv4 or IPv6 CIDR ranges | Each firewall rule can contain either IPv4 or IPv6 ranges, but not both — so dual-stack needs two rules |
Direction is relative to the target, not to you
The phrase from the perspective of the target is the one to hold on to, because it is the source of most early confusion. An ingress rule is about packets arriving at the instances you named; an egress rule is about packets leaving them. So two instances talking to each other are covered by an egress rule on the sender and an ingress rule on the receiver, and both must permit the connection — there is no single rule describing the conversation.
That also explains the asymmetry in what each direction filters on. For an ingress rule the target receives and the source filter describes who may reach it; for an egress rule the target sends and the destination filter describes where it may go. Same two fields, opposite meanings, decided entirely by the direction value.
Scope: enforced at the instance, configured on the network
When you create a firewall rule, you must select a VPC network, and the split that follows is worth stating precisely: while the rule is enforced at the instance level, its configuration is associated with a VPC network. Enforcement is per-VM, which is #61's finding that there is no choke point between subnets. Configuration is per-network, which produces a limit people hit when they build a hub-and-spoke estate: this means that you cannot share firewall rules among VPC networks, including networks connected by VPC Network Peering or by using Cloud VPN tunnels.
A peering carries routes, as #68 described, and it carries no rules at all. Every network in a peered estate needs its own copy of any policy you want applied in it — which is the gap hierarchical and network firewall policies exist to close, and which #73 and #74 take up.
Firewall policy rules and VPC firewall rules both use priority, and the documentation tabulates the differences. A policy rule's range is 0 to 2,147,483,547 against a VPC rule's 0 to 65535; a policy rule has no default while a VPC rule defaults to 1000; and on uniqueness a policy rule must be unique within the policy while a VPC rule can be shared across rules. That last row is why the indeterminacy above exists at all: the VPC firewall model permits duplicate priorities, so avoiding them is a discipline rather than a constraint. 65,536 values and a default in the middle of the range is plenty of room to never need a duplicate.
Why This Architecture Holds Up
VPC firewall rules are stateful, and the consequence is generous in one direction and absolute in the other: when a connection is allowed through the firewall in either direction, return traffic matching this connection is also allowed, and you cannot configure a firewall rule to deny associated response traffic.
That second clause removes a control rather than adding one. If you allow a connection out, you have allowed the reply in, and no rule can countermand it. The matching is exact — return traffic must match the 5-tuple (source IP, destination IP, source port, destination port, protocol) of the accepted request traffic, but with the source and destination addresses and ports reversed — so this is not a hole, but it does mean egress rules are the only place to make that decision.
Tracking is applied more broadly than the word "connection" suggests: Google Cloud implements connection tracking regardless of whether the protocol supports connections. ICMP and UDP exchanges get tracking entries too, which is what makes a reply to a ping work without an explicit ingress rule.
A firewall rule's tracking state is considered active if at least one packet is sent every 10 minutes. So the return path for a long-lived but idle connection is not permanent. A session that goes quiet for longer than that can lose its tracking entry, and the reply that eventually arrives is then evaluated against your rules as though it were unsolicited. Where a protocol has its own keepalive, this never surfaces; where it does not, it looks like an intermittent hang rather than a firewall decision.
The fragment gap, on both paths
This is the part of the page that deserves the most attention, because it is a documented limit on what a port-based rule means. Outbound and inbound are affected differently and both are stated.
On matching: rules do not reassemble, so a TCP rule applies only to the fragment carrying the TCP header, and firewall rules applicable to the TCP protocol do not apply to the subsequent TCP fragments. A deny rule scoped to a TCP port is therefore a statement about first fragments.
On the return path: when a fragmented connection is allowed through the firewall, Google Cloud uses connection tracking to allow only the first fragment of return traffic, and to allow subsequent return fragments, you must add a firewall rule. So a fragmented reply to an allowed request arrives incomplete unless you have written a rule for the rest of it — the opposite failure to the one above, and equally invisible in the rule list.
What no rule can permit
Three limits sit outside the rule system, and an allow rule does not reach them.
| Traffic | Behaviour |
|---|---|
| DHCP offers and acknowledgments | Google Cloud blocks incoming DHCP offers and acknowledgments from all sources except for DHCP packets coming from the metadata server |
| Protocols to external addresses | External IPv4 and IPv6 addresses only accept TCP, UDP, ICMP, IPIP, AH, ESP, SCTP, and GRE packets |
| SMTP on port 25 | By default Google Cloud blocks egress packets sent to TCP destination port 25 of an external IP address, and Google Cloud excludes SMTP traffic over TLS on port 465 or 587 from this block |
The SMTP one is the most likely to cost an afternoon, because it is a default rather than a policy and it is scoped to external addresses only — port 25 to an internal address is unaffected. An egress allow rule for TCP 25 will exist, be correct, and still not deliver mail.
Key Architecture Decisions
| Situation | Decision | Because |
|---|---|---|
| Any rule set at all | Give every rule a unique priority | Duplicates are legal, and equal priority with equal action makes the logged rule arbitrary. |
| A broad deny you intend to be final | Put it at a low number, not merely below the allows | Specificity never overrides priority, in either targets or ports. |
| Reviewing an existing rule set | Sort by priority and read downward | Reading for the narrowest match models a different firewall. |
| Dual-stack workloads | Two rules per intent | A rule carries IPv4 or IPv6 ranges, never both. |
| Controlling what a VM may reach | Decide it in the egress rule | Once a connection is allowed, its response cannot be denied by any rule. |
| Port-level filtering that must be airtight | Terminate at a proxy or load balancer | TCP rules match the first fragment only. |
| Policy that must apply across peered networks | Firewall policies, not VPC firewall rules | Rules cannot be shared between networks, peered or not. |
| Outbound mail | Use 465 or 587 | Egress to TCP 25 on an external address is blocked by default. |
The one to look at today
List the firewall rules in one network sorted by priority and look for two things. First, duplicate priority numbers — each cluster is a place your logs cannot tell you which rule acted. Second, any allow rule with a lower priority number than your broad deny: that is a rule the deny does not constrain, regardless of how narrowly the deny is written, and it is the shape an unintended exposure takes here. Neither is visible if you read the list by name or by what each rule appears to be for.
Closing Thought
Two subsystems in two days have now told the same story. Routes do not break ties by specificity; firewall rules do not break ties by specificity either. In both, priority is the only ordering, in both equal priorities produce a documented indeterminacy, and in both the fix is the same single sentence about assigning unique numbers. Having met it twice, I am inclined to read it as a platform-wide convention rather than a quirk of either page: where Google Cloud evaluates a set of things you wrote, it expects you to have ordered them, and it will not infer an order from how carefully you scoped them.
The fragment behaviour sits differently and is the thing I would most want a reader to carry away. It is not a tie-break or a precedence rule; it is a limit on what a port-based rule can mean, stated in two sentences in the middle of a specifications list. A rule that says "deny TCP 22" reads like a complete statement about SSH and is documented as a statement about header-bearing fragments. Ten posts into this block, the pattern has held every time: the console shows you the resource, and the sentence that bounds it is in the prose.
#71 takes the rules you did not write: the implied rules every network has, and the extra rules the default network ships with.
Comments