Every firewall rule is a decision someone made, at a moment, with incomplete information. Six months later the decision is still in force and the reasoning is gone. That is how a rulebase becomes archaeology.

The discipline is simple and mostly clerical: name the rule for its intent, write down why, test it before and after, and review it on a schedule.

Name for intent, not protocol

A rule called allow-tcp-443 tells you nothing. A rule called student-to-internet tells you what was decided.

<source>-to-<destination>-<service>
student-to-wan
staff-to-infra-dns
campus-to-district-print

Add the ticket or date in a comment field if the platform has one, and keep the detailed rationale in the documentation, not in the rule name. Names are for scanning; documentation is for understanding.

Intent, rule, test, review: the loop that keeps a rulebase honest.

Order by specificity

Rules should read from most specific to most general within a zone pair:

  1. Explicit denies for known-bad or known-unwanted flows.
  2. Specific allows (one host, one service).
  3. Broad allows (a whole zone to a service group).
  4. The default deny, implicit or explicit.

The first match wins, so this ordering makes each rule reachable and each exception visible. The inverse ordering — a broad allow near the top — is how a rulebase becomes unfalsifiable: everything works, so nobody notices that half the rules never fire.

If a rule has zero hits, it is either new, wrong, or shadowed

A newly added rule has zero hits and that is expected. A rule that has been in place for a month with zero hits is telling you something: the traffic never reaches it (shadowed), or the service is unused (removable). Both are worth knowing.

Least privilege, expressed as a table

Write the intent as a table before touching the firewall:

From To Service Why Ticket
Student Infrastructure DNS 53 Name resolution CHG-1042
Student Printers 9100, 631 Lab printing CHG-1042
Student WAN Any Internet, filtered at DNS CHG-1042
Student Everything else Any Deny —

Then implement it literally. The table is also the review artifact: a year later, someone can compare the table to the rulebase and see drift immediately.

Test before and after

Test Before After Proves
Browse from student VLAN Works Works Did not break the default path
Resolve internal name Works Works DNS rule still correct
Print from lab Works Works Printing rule still correct
Reach a server from student VLAN Blocked Blocked Segmentation actually applied
Reach the internet from guest Works Works Guest path untouched

The fourth test is the one people skip. A rulebase that permits everything passes the first three tests and fails the entire point of the change.

Change control that fits a one-person team

  • One change window, after 17:00, as the district already uses.
  • Write the intended end state, not the clicks.
  • Take a config backup before the change.
  • Apply, test the table above, and record the result.
  • Keep the old rule (disabled) for one week, then delete it.

That last step matters: disabling rather than deleting gives you a one-click rollback during the week when problems surface.

Worked example: the new campus

Six rules for a new elementary, expressed as intent:

# Rule Purpose
1 campus-to-district-dns AD and name resolution
2 campus-to-district-print Printing
3 campus-to-wan Internet, filtered
4 district-to-campus-mgmt Monitoring and management
5 campus-iot-to-wan HVAC and cameras to vendor cloud
6 campus-deny-rest Everything else

Apply them, then test from a student device, a staff device, and an IoT controller. If the IoT controller can reach a student device, rule 6 is not doing its job.

What breaks in production

  • The permanent temporary rule. allow-any-any-test from a troubleshooting session, still there two years later.
  • Rules without owners. Nobody knows what depends on them, so nobody removes them.
  • Policy drift. The documentation says one thing; the firewall says another; nobody has compared them.
  • Over-broad service groups. "Allow web" that includes 80, 443, 8080, and 8443 because someone was in a hurry.
  • No default deny. The rulebase ends with allow-all, so every mistake is permissive.

Checklist

  1. Every rule name states source, destination, and intent.
  2. Rules are ordered specific to general, with an explicit deny at the end.
  3. The intent table exists in the documentation and matches the live rulebase.
  4. Each change has a before/after test, including a negative test.
  5. Decommissioned rules are disabled for a week, then deleted.
  6. The rulebase is reviewed quarterly, and the review is recorded.

Next

Writing policy is the easy half. Continue with Auditing a Live Rulebase, where hit counters tell you what the rules actually do.

  • Firewall
  • Policy
  • Change Control
  • Documentation