Course outline

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.

About 4 min readContent reviewed
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.

python Check inert protocol/port cases locally
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")
Expected output
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.

python Check a documentation-address delivery scope
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")
Expected output
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.

Official references