Tutorial
In this tutorial, you'll learn how to use Invariant to analyze a network snapshot and evaluate access policiy rules. You will start with a skeleton snapshot that is ready for upload but contains errors. You will start by identifying and fixing these errors in the snapshot. Once the snapshot is error-free, you will then proceed to build access policies based on the corrected snapshot.
In this article:
- Set up and run Invariant against the tutorial network
- Learn how to spot and correct issues in network models
- Attach a virtual ISP to the network
- Write Invariant rules and examine virtual traceroutes to troubleshoot
20 minute read
Prerequisites
-
Follow the Quick Start guide to:
- Install the Invariant CLI
- Create a free Invariant Account
- Authenticate your CLI session
-
Download the Tutorial Network from Github.
- You can download it from the Github website by selecting Code > Download zip .
- Or use a git client. Official git client download.
That's it, you are now ready to begin.
The completed tutorial can be found at https://github.com/InvariantTech/codelab_solution.
Model Preparation
Open the Tutorial Network. If you haven't already, download it using git.
$ git clone https://github.com/InvariantTech/codelab.git
The repository contains several important directories that make up the structure of a network snapshot on disk.
config
contains all on-premise device configurations.def
contains human-friendly labels for IP addresses.invariant
contains user-defined Invariant rules and human-friendly names for network interfaces.batfish
contains ISP modeling information.
If you haven't already, log in to Invariant by issuing the following command and following the instructions.
$ invariant login
This prints a login URL. Open it in your browser and sign in. Never share the login code with anyone. After authentication, you'll see:
Logged in as [email protected] (tenant=your-organization-name).
First Run
Analyze the snapshot using the invariant run
command.
# Tell the CLI where to find the tutorial directory, named "codelab"
$ invariant run --target path/to/codelab
Uploading snapshot...
Processing... (d1dff68d-7da6-4438-87d8-a12086073823)
# Or just run directly from the tutorial directory
$ cd codelab/
$ ls
batfish configs def invariant
$ invariant run
When the analysis completes, you will see an error about "Map Support data", followed by a list of reports files and how many rows are in each report.
$ invariant run
Uploading snapshot...
Processing... (d1dff68d-7da6-4438-87d8-a12086073823)
Analysis complete.
The following steps were not completed:
Evaluate Snapshot > Evaluate Snapshot > Map Support data
╭───────────────────────┬────────╮
│ Network Information │ Rows │
├───────────────────────┼────────┤
│ nodes │ 9 │
│ interfaces │ 87 │
│ named_structures │ 54 │
│ defined_structures │ 351 │
│ referenced_structures │ 350 │
│ unused_structures │ 5 │
│ vlan_properties │ 12 │
│ hsrp_properties │ 0 │
│ mlag_properties │ 0 │
│ ip_owners │ 48 │
│ undefined_references │ 0 │
│ vrrp_properties │ 8 │
╰───────────────────────┴────────╯
╭───────────────┬────────╮
│ Topology │ Rows │
├───────────────┼────────┤
│ edges │ 34 │
│ layer_1_edges │ 0 │
│ layer_3_edges │ 34 │
╰───────────────┴────────╯
╭────────────────────────────┬────────╮
│ Routing │ Rows │
├────────────────────────────┼────────┤
│ routes │ 253 │
│ bgp_process_config │ 1 │
│ bgp_peer_config │ 2 │
│ bgp_session_compatibility │ 2 │
│ bgp_session_status │ 2 │
│ bgp_edges │ 0 │
│ bgp_ribs │ 1 │
│ ospf_process_config │ 6 │
│ ospf_interface_config │ 36 │
│ ospf_area_config │ 6 │
│ ospf_session_compatibility │ 20 │
╰────────────────────────────┴────────╯
╭───────────────────┬────────╮
│ Setup │ Rows │
├───────────────────┼────────┤
│ unconnected_nodes │ 0 │
│ ignored_lines │ 32 │
│ file_parse_status │ 9 │
│ parse_warnings │ 59 │
│ errors │ 1 │
╰───────────────────┴────────╯
╭────────────────────────┬────────╮
│ Inconsistent Traffic │ Rows │
├────────────────────────┼────────┤
│ subnet_multipath │ 0 │
│ loopback_multipath │ 0 │
╰────────────────────────┴────────╯
╭──────────┬────────╮
│ Probes │ Rows │
├──────────┼────────┤
│ probes │ 3 │
╰──────────┴────────╯
Run 'invariant show <file>' to examine any file.
1 error found.
In Evaluate Snapshot > Map Support data:
Location not found.
For location 'EXTERNAL': Undefined location: 'EXTERNAL'.
The error message at the bottom of the output has to do with configuration we must perform for auto-mapping to appear for the tutorial network. The issue is that the tutorial network snapshot doesn't contain information about how the network is connected to the Internet or any other network (it just contains configs from devices in the tutorial network), and Invariant wants to know where the 'north' side of the network is before it will generate the map. The error suggests we fix by designating one or more interfaces with the special name 'EXTERNAL', but we can also satisfy it by connecting a virtual ISP and virtual Internet to our model! We will do so in the next section.
Before we move on, let's review the data Invariant generated as part of the analysis process. The generated files are divided into multiple sections as follows.
Network Information
provides general details about the network, including nodes and interfaces.Topology
contains reports describing the edges within the network.Routing
contains information about the calculated route tables and the processes involved in their creation.Setup
section contains information about errors and warnings related to model creation.Inconsistent Traffic
contains alternative paths between points that contain inconsistencies.Probes
offers quick virtual traceroutes through the model network.Access Policy
will appear later in the tutorial and include your deny rules and critical flows.
You can examine any of these files using the invariant show
command.
# Displayes all routes across all devices and VRFs
$ invariant show routes
# Alternatively you can pull any file as TSV or JSON
$ invariant show routes --tsv > routes.tsv
$ invariant show routes --json > routes.json
# Fast-JSON is just JSON but without whitespace - saves bytes
$ invariant show routes --fast-json > routes.json
# The 'show' command applies to the most recent snapshot uploaded from this CLI session
# Alternatively you can specify the snapshot ID
$ invariant show routes --snapshot d1dff68d-7da6-4438-87d8-a12086073823
Complete information about the reports can be found in the Output section.
Check For Issues
In this tutorial network, we know that all network configuration files will be parsed reasonably well and all key files are present. But when you use Invariant on your own network, that might not always be the case - you might have forgotten to include the config for a key device, or maybe one of your devices is not actually supported by Invariant. Therefore, a best practice is to examine the network model and confirm that all key devices are present.
Some questions you might ask in your first look of a network model could be:
- Are all key devices present? Devices are called 'nodes'.
$ invariant show nodes
- Are any devices unexpectedly unconnected from the rest of the network?
Note that the analysis looks at Layer 3 (IP) connectivity. Layer 2 devices can sometimes appear disconnected as they do not explicitly participate in the Layer 3 network.
$ invariant show unconnected_nodes
- Did any files I included fail to parse?
We're looking for any file with status ERROR. It's normal for config files to have status PARTIALLY_UNRECOGNIZED - the network model safely ignores many config lines at the parser level.
$ invariant show file_parse_status
Additional files in the Troubleshooting section can be helpful when diagnosing more specific issues. For example ignored_lines
and parse_warnings
can help you locate typos or confirm whether a config line is being ignored. unused_structures
and undefined_references
may point to other typos, obsolete code, or gross misconfigurations within your configs.
Add A Virtual ISP
The tutorial network snapshot doesn't contain information about how the network is connected to the Internet or any other network. It just contains configs from devices in the tutorial network.
Having the model include a virtual ISP can be very useful, in fact, it is critical for the tutorial network, which relies on the ISP to define default external routes.
Attaching the virtual ISP also allows Invariant to know where the 'north' side of the network, which will allow it to generate a map of your network (you can also tell Invariant where 'north' is using 'EXTERNAL' - see below).
Checking internet connectivity
We will begin by using the 'probes' tool to show that the network is indeed missing a default external route.
The probes analysis runs on every network snapshot. It classifies connectivity from each node to a pre-selected list of public DNS providers. Each node can have status 'always', 'never', and 'partial'.
$ invariant show probes
+----+------------+-----------+---------------+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| | target | type | comment | ignore_filters | node_outcomes |
|----+------------+-----------+---------------+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 | 1.1.1.1/32 | ICMP_ECHO | Default probe | True | {'asa': 'never', 'border-1': 'never', 'core-1': 'never', 'core-2': 'never', 'dc-1': 'never', 'dist-1': 'never', 'dist-2': 'never', 'dmzfw-1': 'never', 'dmzsw-1': 'never'} |
| 1 | 4.2.2.2/32 | ICMP_ECHO | Default probe | True | {'asa': 'never', 'border-1': 'never', 'core-1': 'never', 'core-2': 'never', 'dc-1': 'never', 'dist-1': 'never', 'dist-2': 'never', 'dmzfw-1': 'never', 'dmzsw-1': 'never'} |
| 2 | 8.8.8.8/32 | ICMP_ECHO | Default probe | True | {'asa': 'never', 'border-1': 'never', 'core-1': 'never', 'core-2': 'never', 'dc-1': 'never', 'dist-1': 'never', 'dist-2': 'never', 'dmzfw-1': 'never', 'dmzsw-1': 'never'} |
+----+------------+-----------+---------------+------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Set --json to get JSON
See 'show --help' for more options
Without any default external route in the network model, all network devices devices appear to lack connectivity to any public DNS providers. This network expects a default route to be provided by its ISP through BGP. You will need to configure a placeholder ISP and connect it to the border-1 router.
ISP settings are configured by placing a file inside the snapshot named batfish/isp_config.json
. Start by specifying the interfaces used for ISP peering in the borderInterfaces
section. Then, in the ispNodeInfo
section, configure the placeholder ISPs, setting a 'role' and ASN number. The two supported roles are TRANSIT
and PRIVATE_BACKBONE
. The TRANSIT type will model a transit network connecting to the public internet, which is what we want here.
{
"borderInterfaces": [
{
"borderInterface": {
"hostname": "border-1",
"interface": "GigabitEthernet0/1"
}
},
{
"borderInterface": {
"hostname": "border-1",
"interface": "GigabitEthernet0/3"
}
}
],
"ispNodeInfo": [
{
"asn": 64501,
"name": "ISP1",
"role": "TRANSIT"
},
{
"asn": 64502,
"name": "ISP2",
"role": "TRANSIT"
}
]
}
With our virtual ISP configured, run invariant run
again. When the analysis finishes, you should notice the Map Support Data error is resolved. Re-examine the probes test and you will see that connectivity to public DNS providers is now established for all network devices. Problem resolved!
$ invariant show probes
+----+------------+-----------+---------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| | target | type | comment | ignore_filters | node_outcomes |
|----+------------+-----------+---------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 0 | 1.1.1.1/32 | ICMP_ECHO | Default probe | True | {'asa': 'always', 'border-1': 'always', 'core-1': 'always', 'core-2': 'always', 'dc-1': 'always', 'dist-1': 'always', 'dist-2': 'always', 'dmzfw-1': 'always', 'dmzsw-1': 'always', 'internet': 'always', 'isp_64501': 'always', 'isp_64502': 'always'} |
| 1 | 4.2.2.2/32 | ICMP_ECHO | Default probe | True | {'asa': 'always', 'border-1': 'always', 'core-1': 'always', 'core-2': 'always', 'dc-1': 'always', 'dist-1': 'always', 'dist-2': 'always', 'dmzfw-1': 'always', 'dmzsw-1': 'always', 'internet': 'always', 'isp_64501': 'always', 'isp_64502': 'always'} |
| 2 | 8.8.8.8/32 | ICMP_ECHO | Default probe | True | {'asa': 'always', 'border-1': 'always', 'core-1': 'always', 'core-2': 'always', 'dc-1': 'always', 'dist-1': 'always', 'dist-2': 'always', 'dmzfw-1': 'always', 'dmzsw-1': 'always', 'internet': 'always', 'isp_64501': 'always', 'isp_64502': 'always'} |
+----+------------+-----------+---------------+------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Set --json to get JSON
See 'show --help' for more options
External Interfaces (optional)
Now that we have added a virtual ISP, there is no need to set an EXTERNAL location to our network snapshot. This feature tends to be useful in sub-networks that don't connect to any ISP and are not peered with other networks.
You don't need to do this to complete the tutorial, but we'll include the instructions for completeness.
The EXTERNAL location should include all external-facing interfaces in your network. In the tutorial network this is border-1
with interfaces GigabitEthernet0/1
and GigabitEthernet0/3
, which connect to the ISP.
locations:
EXTERNAL:
- devices: border-1
interfaces: GigabitEthernet0/1
- devices: border-1
interfaces: GigabitEthernet0/3
Rules Tutorial
Now that we have a network snapshot in working order, let's write some rules using the Invariant rule language. Here are some key things to know before you begin.
- Structure:
- Rule files should be placed in the
invariant/policies/
directory inside the snapshot. - Network name definitions should be placed in the
def/
directory inside the snapshot.
- Rule files should be placed in the
- Format:
- Rule files are YAML files and must contain a top-level label
access-policy:
containing one or more groups of rules. - Network names definitions can be given as YAML files and must contain a top-level label
networks:
. You can also place Capirca.net
files directly in thedef/
directory.
- Rule files are YAML files and must contain a top-level label
- Execution:
- Invariant will evaluate all rules found in the
invariant/policies/
directory every time a network snapshot is uploaded, such as by runninginvariant run
. - The output
Access Policy
section will contain data about rule evaluation, including a count of how many rules failed. - You can directly execute a single rule using
invariant try-rule
which is much more efficient when adding a new rule.
- Invariant will evaluate all rules found in the
First Rules File
The tutorial network contains VLANs DATACENTER
(172.16.50.0/24
) and VLAN30
(192.168.30.0/24
). Let's write a rule which verifies that SSH traffic originating on hosts in the VLAN30
network can never reach hosts in the DATACENTER
network.
Invariant rules are always organized within a rule file according to this hierarchy:
access-policy > [list of rule groups] > rules > [list of rules]
Rules have a type
which can be one of ingress-deny
, ingress-deny-others
, ingress-critical-flow
, or the egress-
versions of each. Our rule will use the ingress-deny
type as we want to verify that certain traffic is never deliverable and we care about traffic ingressing into the DATACENTER network.
Rule groups always have an ingress-network
or egress-network
depending on the type of rule contained within. This field is inherited by all rules inside the group.
A rule group concerning the DATACENTER network and containing a single ingress-deny rule will start like this:
access-policy:
- name: datacenter-security-policy
ingress-network: DATACENTER
rules:
- type: ingress-deny
The ingress-deny rule needs to specify what traffic should not be permitted - here, it's SSH traffic originating in VLAN30.
source-address: VLAN30
destination-port: SSH
protocol: tcp
It's a good practice to always add comments and team contact information in your rules, so we will add those now. Putting all the parts together, we have our first complete rule:
access-policy:
- name: datacenter-security-policy
comment: Access to the data center is controlled by this policy
owner: [email protected]
ingress-network: DATACENTER
rules:
- type: ingress-deny
comment: VLAN30 must not be able to reach DATACENTER through SSH
source-address: VLAN30
destination-port: SSH
protocol: tcp
Let's create a new snapshot using invariant run
containing our new rule. With the rule in place, the network must be in compliance with your rule. This rule will pass.
$ invariant run
Uploading snapshot...
Processing... (6c334806-126c-4b63-bafe-495b39fdf995)
Analysis complete.
# ... skipped
╭──────────────────────────────────────┬────────╮
│ Access Policy │ Rows │
├──────────────────────────────────────┼────────┤
│ critical_flows_ok │ 0 │
│ critical_flows_violations │ 0 │
│ critical_flows_violations_unenforced │ 0 │
│ critical_flows_skipped │ 0 │
│ critical_flows_details │ 0 │
│ critical_flows_logs │ 0 │
│ policy_ok │ 1 │
│ policy_violations │ 0 │
│ policy_violations_unenforced │ 0 │
│ policy_skipped │ 0 │
│ policy_details │ 3 │
│ policy_logs │ 1 │
╰──────────────────────────────────────┴────────╯
Run 'invariant show <file>' to examine any file.
A new "Access Policy" section appeared in our output. The policy_ok
data should have a single row - that represents your passing rule. If we saw a row in policy_violations
that would indicate a failure.
The policy_detail
data is interesting. It contains three rows even though you only added a single rule. The Invariant rule engine often splits a rule into smaller parts during execution and each part produces its own detail entry.
Open the policy_ok
report. You can confirm your new rule is indeed passing. This report is easier to read using the --json
switch as shown. Create this file using invariant show policy_ok --json > policy_ok.json
.
[
{
"index": 0,
"ok": true,
"skipped": false,
"policy": {
"comment": "Access to the data center is controlled by this policy",
"egress-network": null,
"enforce": null,
"ingress-network": {
"list": [
"DATACENTER"
],
"object": null
},
"name": "datacenter-security-policy",
"owner": "[email protected]"
},
"rule": {
"comment": "VLAN30 must not be able to reach DATACENTER through SSH",
"destination-port": [
"SSH"
],
"protocol": [
"tcp"
],
"source-address": [
"VLAN30"
],
"type": "ingress-deny"
},
"errors": [],
"rule_type": "ingress-deny",
"violations": 0,
"checks": 6,
"enforce": true
}
]
We can see that the rule contents are reflected back in this file. The ok
field tells us the rule passed. enforce
tells us the rule is in effect, which is the default. It's possible to set enforce: false
when you define a rule, which is helpful if tracking progress towards a desired network state.
A Failing Rule
Now let's expand our rule group. Our next rule will verify that VLAN40 access to DATACENTER is categorically denied.
access-policy:
- name: datacenter-security-policy
comment: Access to the datacenter is controlled by this policy
owner: [email protected]
ingress-network: DATACENTER
rules:
- type: ingress-deny
comment: VLAN30 must not be able to reach DATACENTER through SSH
source-address: VLAN30
destination-port: SSH
protocol: tcp
- type: ingress-deny
comment: VLAN40 must not be able to reach DATACENTER at all.
source-address: VLAN40
Re-analyze the network again using invariant run
to evaluate your new rule.
Unlike the first rule, this rule will fail. The policy_violations
data should now contain one row.
When a rule fails, Invariant produces virtual traceroutes which demonstrate the issue. policy_details
contains those traceroutes. Let's look at them to understand the problem.
invariant show policy_details --json > policy_details.json
Search the file for "ok": false
to locate example traceroute for the failing rule.
{
"index": 2,
"ok": false,
"policy": {
"comment": null,
"egress-network": null,
"enforce": null,
"ingress-network": {
"list": [
"DATACENTER"
],
"object": null
},
"name": "datacenter-security-policy",
"owner": null
},
"rule": {
"comment": "VLAN40 must not be able to reach DATACENTER at all.",
"deny-all-except": null,
"destination-port": null,
"protocol": null,
"source-address": [
"VLAN10"
],
"type": "ingress-deny",
"within": null
},
"resolved_as": [
{
"destination_address": [
"172.16.50.0/24"
],
"destination_exclude": null,
"destination_node": null,
"destination_port": null,
"enter_interface": null,
"protocol": null,
"source_address": [
"192.168.10.0/24"
],
"source_exclude": null,
"source_interface": null,
"source_port": null
}
],
"errors": [],
"rule_type": "ingress-deny",
"direction": "INGRESS_DENY",
"start": "@enter(dist-2[Vlan40])",
"traces": //...
// ...
This is the beginning of the detail entry row.
The policy
, rule
, and ok
sections are the same as in the policy_ok
rule we saw above. The resolved_as
section is new - it shows you what IP addresses your network names resolved to.
The start
section is important. It tells the node and interface where the virtual traceroute originated. The content of start
should look something like @enter(dist-2[Vlan40])
. This expression means that the virtual packet originated outside of and entering node dist-2
on interface Vlan40
(@enter
means the packet originated outside of any device).
The traces
section contains the virtual tracetoutes we want. Here is one of them:
{
"action": "PERMITTED",
"detail": {
"arpIp": null,
"filter": "vlan40-in",
"filterType": "INGRESS_FILTER",
"flow": {
"dscp": 0,
"dstIp": "172.16.50.0",
"dstPort": 80,
"ecn": 0,
"fragmentOffset": 0,
"icmpCode": null,
"icmpVar": null,
"ingressInterface": "Vlan40",
"ingressNode": "dist-2",
"ingressVrf": null,
"ipProtocol": "TCP",
"packetLength": 512,
"srcIp": "192.168.40.253",
"srcPort": 49152,
"tcpFlagsAck": 0,
"tcpFlagsCwr": 0,
"tcpFlagsEce": 0,
"tcpFlagsFin": 0,
"tcpFlagsPsh": 0,
"tcpFlagsRst": 0,
"tcpFlagsSyn": 1,
"tcpFlagsUrg": 0
},
"forwardingDetail": null,
"inputInterface": "Vlan40",
"inputVrf": null,
"matchCriteria": null,
"outputInterface": null,
"resolvedNexthopIp": null,
"routes": null,
"sessionAction": null,
"sessionScope": null,
"transformation": null,
"transformedFlow": null
}
},
{
"action": "PERMITTED",
"detail": {
"arpIp": null,
"filter": "internal-to-external",
"filterType": "POST_TRANSFORMATION_INGRESS_FILTER",
"flow": {
"dscp": 0,
"dstIp": "172.16.50.0",
"dstPort": 80,
"ecn": 0,
"fragmentOffset": 0,
"icmpCode": null,
"icmpVar": null,
"ingressInterface": "Vlan40",
"ingressNode": "dist-2",
"ingressVrf": null,
"ipProtocol": "TCP",
"packetLength": 512,
"srcIp": "192.168.40.253",
"srcPort": 49152,
"tcpFlagsAck": 0,
"tcpFlagsCwr": 0,
"tcpFlagsEce": 0,
"tcpFlagsFin": 0,
"tcpFlagsPsh": 0,
"tcpFlagsRst": 0,
"tcpFlagsSyn": 1,
"tcpFlagsUrg": 0
},
"forwardingDetail": null,
"inputInterface": "INSIDE1",
"inputVrf": null,
"matchCriteria": null,
"outputInterface": null,
"resolvedNexthopIp": null,
"routes": null,
"sessionAction": null,
"sessionScope": null,
"transformation": null,
"transformedFlow": null
}
},
Unlike a live traceroute, these virtual traceroutes contain detailed information about the packet's internal progress within each node. The flow
section describes the example packet at the moment it was processed in this step: TCP flags, fragment offset, and packet length are all shown. We can also see how the packet was handled by any firewall filters, including actions like establishing a state and forwarding the packet. You can observe if and how the packet is transformed.
Assessment
Two firewalls are letting the packets through. Fixing either firewall would close off this access and resolve the violation.
Close off this traffic with the following steps:
- Remove line 119 from
config/asa.cfg
- Remove line 216 from
config/dist-1.cfg
- Remove line 216 from
config/dist-2.cfg
Re-analyze the network again using invariant run
after removing these lines to confirm that all violations are resolved. All rules should now pass.
Deny-Others Rule
Deny-others rules express exactly which traffic should be allowed in or out of a subnet within some constraints. A simple deny rule specifies what is not permitted, but a deny-others rule specifies a scope of traffic to deny and then punches holes for exempt traffic.
To see this in action, create a new policy that covers VLAN40. In this policy, you will blanket deny all internal network access to VLAN40 over TCP, then carve out an exemption for traffic originating from a single host (ALICE_DESKTOP). Create the policy as follows:
access-policy:
- name: sensitive-vlan-policy
comment: Policy for the sensitive vlan.
ingress-network:
destination-address: VLAN40
destination-exclude: VLAN40_IF
rules:
- type: ingress-deny-others
comment: Limit ingress to only SSH from Alice's desktop
within:
- protocol: tcp
source-address: RFC1918
deny-all-except:
flows:
- comment: Internal SSH access
source-address: ALICE_DESKTOP
destination-port: SSH
protocol: tcp
Re-analyze the network again using invariant run
. Your new rule should pass. The existing network configuration permits access from Alice's desktop to VLAN40.
Be aware that deny-others rules do not test whether the exempted traffic is actually deliverable. The ‘deny-all-except’ section does not assert that the exempted flows are deliverable. It asserts that flows outside the exempted traffic are never deliverable.
To actually assert that a key traffic flow is deliverable, see critical flow rules in the next section.
You can confirm that your deny-others rule does not test whether the exempted traffic is actually deliverable by making a change that would reject traffic from ALICE_DESKTOP to VLAN40. If you delete line 64 from config/dist-1.cfg
and config/dist-2.cfg
and then re-run invariant run
you should observe that there are no violations. However, if you restore the deleted line, but then modify it to allow traffic from some other IP instead of 192.168.10.98
(which is ALICE_DESKTOP), Invariant should flag this unpermitted access as a violation.
Critical Flow Rule
To assert that a key traffic flow must be allowed through the network, use a critical flow rule. Critical flow rules will fail if a change to the firewall or routing partially or completely prevents delivery of the target flow.
Create a policy which asserts that VLAN10 can connect to the data center.
access-policy:
- name: vlan10-security-policy
comment: Access exiting VLAN10 is controlled by this policy
owner: [email protected]
egress-network: VLAN10
rules:
- type: egress-critical-flow
comment: VLAN10 must be able to reach the data center.
destination-address: DATACENTER
destination-port: HTTPS
protocol: tcp
Re-analyzing the network using invariant run
will show this test passes. One row should appear in the critical_flows_ok
report.
Reports that start with critical_flows_
will have the same schema as their policy_
counterparts. One key difference: a passing critical flow rule will always have at least one virtual traceroute in the critical_flows_details
report, while a deny or deny-others rule will only have a virtual traceroute when it fails.
A failing critical flow rule usually has one or more virtual traceroutes showing where traffic was terminated. A virtual traceroute might not be generated if the originating host has no route for the packet.
Next steps
Now that we are at the end, try adding more policies and rules to the codelab and try modifying the configs to introduce violations. You may also want to try adding custom probes. Take time to experiment and learn the ins and outs of everything Invariant offers.