Guide: critical-flow rules
Critical-flow rules in Invariant are designed to assert that specific, essential network traffic must always be successfully delivered. Unlike simply checking for a single path, Invariant exhaustively analyzes your network's digital twin to find any scenario—be it routing configurations, ACLs, or firewall policies—that could prevent this critical traffic from reaching its destination. This provides a strong guarantee for the reachability of your vital services.
How Invariant Evaluates Critical Flow Rules
The evaluation process involves several key steps:
- Digital Twin Creation: Invariant, leveraging Batfish, constructs a detailed model of your network from the provided configuration files.
- Definition Resolution: Any named networks (e.g.,
DATABASE_SERVERS
), services (e.g.,HTTPS
), or locations (e.g.,DMZ_ENTRY_POINTS
) used in your rule are resolved to their concrete IP addresses, ports, and interfaces. - Start Location Inference:
- For
egress-critical-flow
rules, ifsource-interface
orenter-interface
is not explicitly defined, Invariant intelligently infers potential starting interfaces based on thesource-address
. - For
ingress-critical-flow
rules, theingress-network
definition implicitly defines the destination, and Invariant searches for flows from any valid source towards it.
- For
- Exhaustive Flow Search: Invariant performs a comprehensive search for all possible packets that match your critical flow definition. It looks for any instance where such a packet could be dropped, denied, or misrouted.
- Outcome:
- If any packet defined by the rule fails to be delivered via any possible path, the critical-flow rule is marked as a violation.
- If all packets defined by the rule can be successfully delivered across all potential paths, the rule passes.
Defining Critical Flow Rules
Critical flow rules are defined within an access-policy
block in your invariant/policies/
YAML files.
Key characteristics and fields:
type
: Must be eitheringress-critical-flow
(for traffic destined to the policy'singress-network
) oregress-critical-flow
(for traffic originating from the policy'segress-network
).- Traffic Specification: Uses fields like
protocol
,source-address
,destination-address
,source-port
,destination-port
to define the traffic. - Interface Specificity (Optional):
enter-interface
: Foregress-critical-flow
, specifies that traffic must originate by entering these specific interfaces (useful for testing traffic from external sources or specific network segments).source-interface
: Foregress-critical-flow
, specifies that traffic must originate from these specific interfaces (using their configured IPs).
Example ingress-critical-flow
:
This rule asserts that any host should be able to reach the DNS_SERVERS
network on UDP port 53.
access-policy:
- name: core-dns-reachability
comment: Ensure DNS servers are reachable
owner: netops@example.com
ingress-network: DNS_SERVERS # Target network for ingress
rules:
- type: ingress-critical-flow
comment: All internal networks must reach DNS
source-address: RFC1918 # From any internal IP
destination-port: DNS # Standard DNS service
protocol: udp
Example egress-critical-flow
:
This rule asserts that hosts in APP_SERVERS_VLAN
must be able to connect to DATABASE_SERVERS
on TCP port 1433.
access-policy:
- name: app-to-db-connectivity
comment: Critical application to database flow
owner: app-team@example.com
egress-network: APP_SERVERS_VLAN # Source network for egress
rules:
- type: egress-critical-flow
comment: App servers must reach database servers on SQL port
destination-address: DATABASE_SERVERS
destination-port: MSSQL_TCP # Assuming MSSQL_TCP is defined as TCP/1433
protocol: tcp
Interpreting Results
After an invariant run
, you'll primarily look at these reports:
critical_flows_ok
: Lists all critical flow rules that passed.critical_flows_violations
: Lists all enforced critical flow rules that failed.critical_flows_violations_unenforced
: Lists failing critical flow rules that were marked withenforce: false
.critical_flows_details
: This is the most crucial report for debugging. It provides example virtual traceroutes for both passing and failing rules, showing why a flow succeeded or failed.
Strategic Application
- Core Services: Ensure core network services like DNS, NTP, and authentication servers are always reachable.
- Application Connectivity: Validate critical application-to-application or application-to-database communication paths.
- Broad vs. Narrow Rules:
- Broad rules (e.g., "any internal user to any web server on HTTPS") provide wide coverage but can be harder to debug if they fail due to the many potential paths and policies involved.
- Narrow/Canary rules (e.g., "specific_critical_server_A to specific_critical_server_B on specific_port") are easier to pinpoint failures for and are excellent for high-priority services.
- Complement Deny Rules: Critical flow rules ensure what must work, while
deny
ordeny-others
rules ensure what must not work, providing a comprehensive security and connectivity posture.
Understanding Traceroutes
Invariant's virtual traceroutes provide a deep, hop-by-hop analysis of how packets traverse your network model. This is far more detailed than a live traceroute.
Accessing Traceroutes: The critical_flows_details
Report
The critical_flows_details
report is your primary tool for understanding the behavior of critical flow rules. Each row in this report corresponds to a specific check or an example flow Invariant analyzed for one of your rules.
Key fields in critical_flows_details
:
flow
/flow_str
: Describes the specific example packet (source/destination IPs, ports, protocol, etc.) that was traced.traces
: An array containing one or more trace paths. Multiple paths can exist if Equal-Cost Multi-Path (ECMP) routing is in play.disposition
(within each trace): The final outcome for that specific path (e.g.,ACCEPTED
,DENIED_IN
,NO_ROUTE
).
Decoding a Trace
Each trace within the traces
array is a list of hops
. Each hop
represents a device (router, firewall, switch) in the packet's path.
Each hop
contains a list of steps
, detailing the packet's processing on that device:
RECEIVED(Interface)
: Indicates the interface on which the packet arrived at the current hop.- Example:
{"action": "RECEIVED", "detail": {"inputInterface": "GigabitEthernet0/0"}}
- Example:
PERMITTED(FilterName (FilterType))
: The packet was allowed by the specified ACL or firewall rule (FilterName
) applied at a particular stage (FilterType
, e.g.,INGRESS_FILTER
).- Example:
{"action": "PERMITTED", "detail": {"filter": "acl_allow_ssh (INGRESS_FILTER)"}}
- Example:
DENIED(FilterName (FilterType))
: The packet was blocked by the specified filter. This is a common reason for critical flow rule violations. The trace ends here for this path.- Example:
{"action": "DENIED", "detail": {"filter": "acl_block_all (EGRESS_FILTER)"}}
- Example:
FORWARDED(Forwarded out interface: ..., Routes: [...])
: A routing decision was made.outputInterface
: The interface the packet will be sent out of.resolvedNexthopIp
: The next-hop IP address.routes
: A list of routes from the RIB that matched the packet's destination.- Example:
{"action": "FORWARDED", "detail": {"outputInterface": "Serial0/0", "resolvedNexthopIp": "10.1.1.2", "routes": [{"protocol": "ospf", "network": "0.0.0.0/0", ...}]}}
NO_ROUTE(Discarded)
: No route was found in the RIB for the packet's destination. The packet is dropped. This is another common reason for critical flow violations.- Example:
{"action": "NO_ROUTE", "detail": {"type": "Discarded"}}
- Example:
NEIGHBOR_UNREACHABLE(Discarded)
: The next-hop IP address was unresolvable (e.g., ARP or ND failure in the model). Packet dropped.ACCEPTED(Interface)
: The packet was delivered to an interface on the device itself (e.g., destined for a loopback IP, or a service running on the router). This is a successful disposition.DELIVERED_TO_SUBNET(Output Interface: ..., Resolved Next Hop IP: ...)
: The packet was successfully forwarded to the directly connected subnet of the destination IP. This is a successful disposition.EXITS_NETWORK(Output Interface: ..., Resolved Next Hop IP: ...)
: The packet was forwarded out of an interface that leads to a network segment not modeled by Invariant (e.g., towards the internet via an ISP link). This is generally a successful disposition if the destination is external.
Analyzing Traces for Critical Flows
- Passing Rule: For a rule in
critical_flows_ok
, thetraces
incritical_flows_details
will show paths ending in a successful disposition likeACCEPTED
,DELIVERED_TO_SUBNET
, orEXITS_NETWORK
(if appropriate). - Failing Rule: For a rule in
critical_flows_violations
, thetraces
will show the exact hop and step where the failure occurred (e.g., aDENIED
step by an ACL, or aNO_ROUTE
step).
Example: Interpreting a critical_flows_details
Snippet
Let's consider a simple critical flow rule:
# Invariant/policies/example.yaml
access-policy:
- name: web-server-access
owner: web-team@example.com
ingress-network: WEB_SERVER_SUBNET
rules:
- type: ingress-critical-flow
comment: Ensure internal users can access web server via HTTPS
source-address: INTERNAL_USERS_SUBNET
destination-port: HTTPS
protocol: tcp
Scenario 1: Passing Rule
A snippet from critical_flows_details
(in JSON) for a passing flow might look like:
{
"flow_str": "tcp src_ip=192.168.1.50 dst_ip=10.10.10.100 dst_port=443",
"traces": [
{
"disposition": "DELIVERED_TO_SUBNET",
"hops": [
// ... initial hops ...
{
"node": "core-switch-01",
"steps": [
{"action": "RECEIVED", "detail": {"inputInterface": "GigabitEthernet1/0/1"}},
{"action": "PERMITTED", "detail": {"filter": "ALLOW_INTERNAL_ACCESS (INGRESS_FILTER)"}},
{"action": "FORWARDED", "detail": {"outputInterface": "GigabitEthernet1/0/24", "resolvedNexthopIp": "10.10.10.100", "routes": [{"protocol": "connected", "network": "10.10.10.0/24", ...}]}},
{"action": "TRANSMITTED", "detail": {"outputInterface": "GigabitEthernet1/0/24"}}
]
},
{
"node": "web-server-01.example.com", // Assuming host modeling
"steps": [
{"action": "RECEIVED", "detail": {"inputInterface": "eth0"}},
{"action": "ACCEPTED", "detail": {"interface": "eth0"}} // Delivered to host
]
}
]
}
// ... potentially other successful paths if ECMP exists ...
]
}
This trace shows traffic from 192.168.1.50
(part of INTERNAL_USERS_SUBNET
) successfully reaching 10.10.10.100
(part of WEB_SERVER_SUBNET
) on port 443. It was permitted by the ALLOW_INTERNAL_ACCESS
filter on core-switch-01
and finally ACCEPTED
by the web server.
Scenario 2: Failing Rule (ACL Denial)
A snippet for a failing flow, for instance, if an ACL unexpectedly blocks the traffic:
{
"flow_str": "tcp src_ip=192.168.1.50 dst_ip=10.10.10.100 dst_port=443",
"traces": [
{
"disposition": "DENIED_IN",
"hops": [
// ... initial hops ...
{
"node": "firewall-01",
"steps": [
{"action": "RECEIVED", "detail": {"inputInterface": "inside_interface"}},
{"action": "DENIED", "detail": {"filter": "BLOCK_UNTRUSTED_HTTPS (INGRESS_FILTER)"}} // Failure point
]
}
]
}
]
}
In this case, the traffic was DENIED
by the filter BLOCK_UNTRUSTED_HTTPS
on firewall-01
. This immediately tells you where to investigate the misconfiguration.
Edge Case: No Traceroute Generated
Occasionally, a critical flow rule might fail, but critical_flows_details
might not show a full traceroute, or the trace might be very short (e.g., only showing the origin node with a NO_ROUTE
disposition). This typically means:
- No Route at Origin: The starting device/location for the flow doesn't even have a route towards the destination. The packet can't begin its journey.
- Misconfigured Start Location: The
source-address
,enter-interface
, orsource-interface
in your rule might not resolve to any valid points in your network model from which the flow could realistically start.
Carefully examine the flow
details and the initial hops (if any) to diagnose such issues.