skip to content

In a stateless subnet filter evaluated in numbered order, what happens when an explicit deny sits ahead of a broader allow?

level: middleimportance: should knowfreq 56%

answer

  1. position is semantics, not documentation
  2. first match wins
  3. evaluation stops at the matching rule
  4. a deny below a broad allow is dead
  5. allow-only sets have no order

basics

~20 s

Evaluation stops at the first rule that matches, so the deny wins and the broader allow is never reached for that traffic. Reverse the order and the deny becomes dead configuration that the console still shows and nothing enforces.

solid answer

~40 s

An ordered stateless list is **first-match-wins**: the filter walks it in numbered order, applies the first rule whose direction, protocol, port range and source match, and stops. A narrow deny ahead of a broad allow therefore removes that slice from the allow. Put the same two rules the other way round and the deny is unreachable — it is still stored, still displayed, and never evaluated, which is why a review that reads rules as a set rather than a sequence passes a filter that enforces nothing. The traffic that matches **no** rule hits the list's **implicit deny** at the end. The workload-attached stateful set works differently: it is normally allow-only, so there is no deny to order, and you forbid something by not allowing it.

code

yaml · 22 lines
yaml
# stateless subnet filter, inbound direction, first match wins
- order: 100
  direction: inbound
  protocol: tcp
  portRange: 443
  source: 0.0.0.0/0
  action: allow

- order: 200
  direction: inbound
  protocol: tcp
  portRange: 443
  source: 203.0.113.0/24
  action: deny        # unreachable: order 100 already matched

# corrected: give the deny a lower position than the allow
- order: 50
  direction: inbound
  protocol: tcp
  portRange: 443
  source: 203.0.113.0/24
  action: deny

go deeper

for a junior

Recall the two facts that matter: the list is walked in order and the first matching rule decides, and anything matching no rule is dropped by the implicit deny at the end.

for a middle

Explain why the same two rules behave differently when swapped, and why a deny below a broad allow is unreachable configuration. Then contrast it with the allow-only workload layer, where there is no order and no deny.

for a senior

Demonstrate the review habit: for each deny, name the rules above it and show none already matches; test the deny rather than reading it. Mention insertion gaps and why renumbering during an incident introduces exactly this defect.

for a principal

Own the placement standard across teams: coarse denies on the ordered layer, precise allows on the workload layer, and a rule that no change request may be closed on the strength of a rule appearing in a listing.

## First match wins, and that is the whole mechanism A stateless filter attached to a subnet holds an **ordered list**. Each rule carries a position, a direction, a protocol, a port range, a source or destination range and an action. When a packet crosses the boundary, the filter walks the list in position order, tests each rule against the packet, and the **first rule that matches decides** — allow or deny — and evaluation stops there. Nothing after the matching rule is consulted. Two consequences follow, and both show up in real reviews: 1. A **narrow deny placed ahead of a broad allow** works exactly as intended. The denied slice matches first and is dropped; everything else falls through to the allow. 2. The **same two rules in the opposite order** produce a filter with a rule in it that can never fire. The broad allow matches first, so the deny is unreachable. It is still stored, still listed, and enforces nothing. The second case is the dangerous one, because the configuration *reads* correct. Someone asked to "block that range" adds a deny, the change is accepted, the listing shows it, and the traffic continues. A reviewer who reads the rules as an unordered set — the way the workload-attached layer genuinely is — approves it. ## The end of the list A packet that matches no rule at all is dropped by the list's **implicit deny**. This is why an ordered filter is not "open by default" even when it contains only allow rules: the default outcome of falling off the end is refusal, not permission. It is also why an over-narrow list breaks things quietly — the traffic nobody thought about hits the implicit deny and produces the same silent drop as any other missing rule. ## Numbering, gaps and edits Positions are usually written with **gaps** — tens or hundreds rather than consecutive integers — so that a rule can be inserted between two existing ones later without renumbering the list. A list numbered 1, 2, 3 forces a rewrite the first time something has to go in the middle, and a rewrite under time pressure is how ordering defects get introduced. Leave room deliberately. ## The other layer has no order at all | | Ordered stateless filter on the subnet | Stateful rule set on the workload | |---|---|---| | Evaluation | first match wins, in position order | the set as a whole, order irrelevant | | Deny rules | can be written, and position decides | normally not expressible at all | | Default for unmatched traffic | implicit deny at the end of the list | implicit deny — anything not allowed | | How you forbid something | write a deny **ahead of** the allow | remove or narrow the allow | That difference matters when someone asks you to block a specific caller. On the stateless layer, you add a deny and you must place it correctly. On the workload layer there is usually nothing to add — the only way to forbid a caller that a broad allow currently permits is to make that allow narrower. Teams that do not know this write a "deny rule" on the workload layer, find no such option, and widen something else instead. Designs do differ on the details. Some platforms evaluate strictly in position order as described; others evaluate the whole list and let any matching deny win regardless of position. Both exist, and the safe habit is the same either way: **place denies ahead of the allows they are meant to carve out**, which is correct under both models. ## What to check in a review - For every deny, find the rules above it and confirm none of them already matches the traffic the deny is meant to stop. - For every broad allow, ask what it silently re-permits that a deny further down was supposed to remove. - Confirm the list has insertion gaps, so the next urgent change does not renumber it. - Confirm nobody is relying on a deny at the workload layer, where the expressible action is normally allow only. - Test the deny rather than reading it: the rule being present is not evidence that it fires. The short version to say out loud: a stateless list is a sequence, not a set; position is semantics, not documentation; and a deny that sits below an allow covering the same traffic is configuration that exists only to reassure the person who wrote it.

  • The filter contains only allow rules. Is traffic that matches none of them permitted?
    No. Falling off the end of the list is a drop — the implicit deny. A list of allow rules is therefore a closed policy, not an open one, and the usual failure is over-narrowness rather than over-permissiveness: some flow nobody enumerated matches nothing and is silently discarded.
  • Why are rule positions usually numbered in tens or hundreds rather than 1, 2, 3?
    To leave insertion gaps. Ordering is semantics here, so inserting a rule between two existing ones must not require renumbering the list. Consecutive numbering forces a rewrite the first time something has to go in the middle, and rewriting an ordered list under incident pressure is how a deny ends up below its allow.
  • A reviewer asks you to block one caller at the workload-attached layer. What do you tell them?
    That the layer is normally allow-only, so there is no deny to write. The caller is permitted because some allow is broader than it should be, and the fix is to narrow that allow — typically by referencing a specific caller group instead of a wide address range. Adding a deny elsewhere would not remove the existing permission.

saying these in an interview costs you the question

  • Believes a deny wins wherever it sits in an ordered first-match list
  • Adds a deny at the end of a list that already allows the traffic
  • Thinks numbering is cosmetic and every rule is evaluated together
  • Assumes the workload's allow-only rule set can express a deny
  • Expects traffic matching no rule at all to be permitted
  • Treats the rule being present as evidence that it fires