5. Egress control
5.2 Traffic flow documentation: a picture per application and per zone
Requirements: R29.
Section 5.1 chose the hub Firewall as the egress decision of the default stateless cell. Its price is granularity: the Firewall’s rules take a zone’s IP group as their source, so a destination one application asked for is open to every application of its zone, and the Firewall itself can never say which application uses a rule - its log shows pod addresses, never a namespace. Azure has no view that answers “what does application X talk to, and why is this rule here?”.
The platform can answer it, because its rules are not written at the Firewall. Every rule has a source: the egress agent (section 6.1) writes one file per application namespace with the destination, the reason it found in the application’s configuration and the evidence (the denied flows, the pod, the manifest line), and a human merges it. Join those files with the application’s own configuration in Git and the picture draws itself. That is what the documentation agent does.
What is documented, and from where
Everything comes from Git. Nothing is read from a cluster, the Firewall or Azure, so the documentation is the declared state - what is merged - and can be regenerated at any commit:
| Source | Where | What it gives |
|---|---|---|
| The applications’ rule files, one per application namespace | Next to the Firewall rules (section 5.1) | Per application: destination, port, protocol, reason, evidence, the zone, the contact, and the Firewall rule it is rendered into |
| The cells’ and the environment’s configuration | The cluster IaC | Which namespaces run in which cells, their team and contact, where their configuration is (Flux source and path), the zone and its domain, the home cell |
| The application’s Flux Kustomization at that path | The team’s repository (section 3.3) | Workloads, replicas, images, every URL in the pods’ environment and arguments, routes (hostnames, gateway, backends), services - the rendered manifests |
| The zone allow-lists | The platform repository | Names open to every application of the zone (the Firewall’s zone rule collection; the same list the pull request check reads) |
| The egress agent’s open pull requests | The rule files’ repository (optional) | Destinations proposed but not merged yet |
The pictures
Two pictures, both Mermaid in Markdown pages, because GitHub renders Mermaid inline: no rendering step, no binary files in Git, a readable diff when a rule changes, and the same source the design uses.
Per application (docs/traffic-flows/<spoke>/<namespace>.md, example below), from the client (Traffic Manager
and the Internet for an external zone) to every destination outside the landing zone, with the hostnames’ Flux
variables substituted per cell. Each destination carries a status, the one fact the Firewall cannot give:
| Status | Colour | Meaning | What to do |
|---|---|---|---|
| allowed | green | A rule in the application’s file, and the application’s configuration names the destination (a URL in its environment or arguments, or the name in its manifests) | Nothing: the rule has a source |
| allowed, not in the configuration | amber | A rule in the application’s file that nothing in its configuration names | A hidden dependency (a library, a base image, a sidecar) to document - or a rule nobody needs any more, to remove. The agent reports it as a finding |
| denied: no rule | red, dashed | The configuration names a destination that no rule and no zone allow-list allows | The Firewall denies it; the egress agent proposes the rule from the denied flows, a human merges it |
| proposed | purple, dashed | In an open pull request of the egress agent | Review the pull request |
| zone allow-list | teal | On the zone’s allow-list, open to every application of the zone. On an application’s page only when its configuration names it; otherwise it is the zone page’s business | Nothing |
| platform rule | grey | A rule of the platform for every cell of the exposure (the registry, Grafana Cloud). Likewise only when named | Nothing |
Below the picture, the page lists the same destinations with the rule’s reason, where the configuration names the destination, and the rule’s evidence; then the routes (hostnames per cell, gateway, backends), the workloads and notes (a cell that syncs another overlay of the same repository, a namespace whose configuration was not available).
Per zone (docs/traffic-flows/<spoke>/zone-<zone>.md): the union of what the Firewall opens for the zone,
and the names applications declare that no rule allows yet. The zone page also notes when the platform repository’s allow-list and the Firewall’s
allow-list of the zone differ - the Firewall enforces one, the pull request check reads the other. The shared names
live here on purpose: an application’s page shows only what the application itself asked for or names, so a reviewer
sees its own footprint without the zone’s noise. This is the picture section 5.1’s “applications of one zone share the
zone’s destinations” needs: a reviewer of the next egress pull request sees what the zone already reaches and who
brought each name in, and the Firewall’s owners see the zone’s whole exposure on one page. An index page lists the
zones and applications of the spoke.
An example of each picture, for an application int-se-orders with one destination of its own in a cell where the
Firewall decides:
The zone int-se - the application’s rule, the zone allow-list and the platform’s rules:
The documentation agent
The agent follows the rules of section 6.1: read-only sources, Git as its only write path, one pull request per run, tier low (documentation). As with the egress agent, the model is the expensive part and does only what needs judgement:
- Generation is code, not a model. Without a timestamp a run that changes nothing writes nothing, and the check mode is the pull request check of the rule files’ repository, so the pages never fall behind the rules.
- The model explains the findings: when a page changed, the documentation agent runs the generator into its
workspace, looks briefly for the explanation of each amber or red destination in the application’s repository, the
rule’s evidence and the open issues, and opens the pull request with the pages and its findings. It changes
nothing but
docs/traffic-flows/; its grants hold no rule file, no allow-list, no application repository. Proposing a rule stays the egress agent’s job, merging it a human’s. - Triggers: a merged pull request to the rule files or to an application’s configuration, and a weekly schedule. The generation needs no credential and no cluster; the model step reaches the model endpoint over its private endpoint.
The pages live next to the rules they document, in docs/traffic-flows/<spoke>/ of the rule files’ repository:
that is where the egress agent’s pull requests arrive and where the Firewall’s owners review
(section 5.1). A company
that keeps its firewall rules in the firewall team’s repository keeps the pages there too;
the generator takes the repositories as parameters.
The amber status is the review the pictures are for: a rule that no configuration explains is either a hidden dependency to document or a platform flow in an application’s file (a telemetry agent’s usage statistics reported from every node, for example), which moves to the platform’s rules of the exposure. Such a removal is a pull request the documentation agent cannot open itself: a rule’s removal is the egress files’ owners’ decision.