Linux Security / Intermediate: service, policy, and lifecycle boundaries / Lesson 9
Model network policy without applying firewall rules
Define direction, endpoint scope, rule order, and default decisions with inert packet records.
On this page
Lesson goals
- Separate a local rule model from nftables hook and chain enforcement.
- Test legitimate traffic and denied alternatives without sending packets.
Start with the required communication
A report worker may need no network access, or a specific approved destination for delivery. Define the source role, direction, protocol, endpoint, and business purpose before writing rules. Inbound host access, forwarded container traffic, and local output can cross different processing paths. An application permission does not replace a network boundary, and an open port does not prove a healthy or authorized service. The course uses invented packet dictionaries and documentation addresses only. It performs no socket, DNS lookup, connection, scan, packet capture, firewall query, or rule change.
Use a deliberately small ordered-rule model
The first program evaluates exact protocol/port matches in order and then uses a declared default. It accepts one fictional HTTPS delivery case and denies an SSH and an unmatched case. This is not nftables syntax or a netfilter simulator. Actual nftables behavior depends on address family, base chains, hooks, priorities, policies, state, and other chains; an accept in one place is not a universal final decision across the whole ruleset. Keep those boundaries explicit rather than assuming one Python first-match loop validates a real firewall.
rules = [("TCP", 443, "allow"), ("TCP", 22, "deny")]
def decide(protocol, port):
for expected_protocol, expected_port, verdict in rules:
if (protocol, port) == (expected_protocol, expected_port):
return verdict
return "deny"
assert decide("TCP", 443) == "allow"
assert decide("TCP", 22) == "deny"
assert decide("UDP", 443) == "deny"
print("three fictional ordered-rule decisions checked")
three fictional ordered-rule decisions checked
The complete program compares strings and integers only. It sends no traffic and installs no rule. Port 443 is a classroom contract choice and the program does not verify TLS, destination identity, application authorization, or real firewall behavior.
Include endpoint and direction rather than a port alone
A rule allowing every destination on one port can grant more communication than a workload needs. The second program checks an exact invented direction and destination alongside the protocol and port. It remains a classroom predicate, not a DNS-aware egress control or identity policy. Real endpoints may change addresses, use proxies, or depend on separately approved infrastructure. Document those dependencies rather than allowing everything as an unexplained fallback. A documentation address in an example is not a real target, and matching it in memory provides no evidence about routing, reachability, or encryption.
def approved_delivery(packet):
return (packet["direction"], packet["protocol"], packet["destination"], packet["port"]) == ("out", "TCP", "192.0.2.20", 443)
base = {"direction":"out", "protocol":"TCP", "destination":"192.0.2.20", "port":443}
assert approved_delivery(base) is True
assert approved_delivery({**base, "direction":"in"}) is False
assert approved_delivery({**base, "destination":"192.0.2.21"}) is False
print("declared direction and destination narrow the model")
declared direction and destination narrow the model
The complete program evaluates three in-memory dictionaries. The address is reserved example data and never contacted. No IP lookup, network namespace, routing, firewall state, or external service is examined.
Review state, families, and alternate paths
IPv4 and IPv6 coverage, established-flow handling, local sockets, forwarding, containers, and manager-generated rules can change the effective boundary. A single visible chain may not represent the whole path. Do not disable IPv6, flush rules, or broaden administration access as a generic repair. Use a reviewed architecture and an approved disposable verification plan that preserves the required management and recovery path. This lesson applies no network configuration. Its purpose is to show which assumptions a real policy review must resolve before claiming that a selected traffic pattern is blocked or allowed.
Validate policy reasoning separately from deployment
Choose meaningful positive and negative cases, default behavior, rule-order conflicts, and alternate-family or direction cases relevant to the design. Record expected decisions and actual approved observations without generating real traffic for this course. A locally passing predicate is useful evidence about the predicate only. Actual nftables parsing, loaded rule order, routing, application identity, and remote response remain unexecuted. Keep the report precise about the selected reference and model scope, so the exercise cannot be mistaken for a host firewall assessment or a ready-to-apply universal ruleset.
Common errors and fixes
A port allow is called application authorization
- Why it happens
- Transport policy is expanded into business identity and permission.
- How to fix it
- Review destination, protocol, identity, and application scope separately.
- Prevent it next time
- Attach a specific claim to each layer.
One chain proves all traffic paths
- Why it happens
- Hooks, priorities, families, and generated rules are omitted.
- How to fix it
- Review the complete relevant processing path.
- Prevent it next time
- Keep loaded-state and architecture evidence explicit.
A Python verdict is called a firewall test
- Why it happens
- An inert model is mistaken for kernel enforcement.
- How to fix it
- State that no rules or traffic were exercised.
- Prevent it next time
- Plan platform verification only in an approved isolated environment.
Check your understanding
1. The model allows TCP443 to any host. Why may that exceed the requirement?
Show worked solution
The workload may need one approved delivery destination rather than arbitrary outbound communication. Specify endpoint and direction assumptions and review dynamic-address dependencies. A port-only match does not establish encryption or permission, and the course performs no actual delivery or firewall operation.
2. Can an accept decision in a simplified chain prove the real packet is accepted everywhere?
Show worked solution
No. Other hooks, base chains, policies, routing, and family-specific rules can still matter. The local loop is deliberately not a netfilter interpreter. Preserve its narrow decision result and keep actual loaded-path enforcement unverified until separately authorized evidence exists.
Key takeaways
- Network policy needs direction, endpoint, protocol, and purpose.
- A simplified ordered model is not a kernel ruleset.
- Actual loaded paths require separate authorized evidence.