Air-Gapped Sites — Defence and Government
Showing with evidence that nothing leaves the network
Goal
Using only the product configuration files and three kinds of records, judge "there is no unauthorized outside communication" and produce a summary that can be submitted to the audit as is. The scope you confirmed and what you could not confirm must be in the same document.
Why it matters
In a delivery to a network-separated environment, the sentence "nothing goes out" is not accepted. Products include update checks, license checks, remote diagnostics, usage statistics, and time synchronization, and most are on by default.
This lab Pod has no kernel permissions at all, so neither tcpdump nor iptables works. That means there is no way to capture and show packets in the first place. In real sites too, that place is filled by the firewall policy copy and device logs that the network team hands over, and the material a developer can submit is configuration and records. So this lab makes its judgment based on settings and records. This is the assumption of this lab.
More important than getting the numbers right is telling three things apart. Items hidden with comments versus items that are on by default, blocked attempts versus communication that went out, and what you cannot yet know because it cannot be checked by name.
Steps
- With
python3 /root/egress/mkdata.py, create the material that will be the basis of the judgment. Out come 3 configurations, an allowlist, and proxy, DNS, and configuration change records. - Extract every address pointing outside from the configurations and write it in /root/egress/endpoints.tsv as
host port scheme state source. - Check against the allowlist and sort into three branches in /root/egress/classify.tsv as
host port state verdict. - Count the attempts to disallowed destinations in the proxy records and write them in /root/egress/attempts.tsv as
host port attempts ok auth blocked. - Write the destinations that appear only in the query records in /root/egress/dns-only.tsv as
host queries nxdomain, and leave your own direct observation of this Pod's name resolution in /root/egress/resolver.txt. - Link the three records by time and write them in /root/egress/timeline.tsv as
host change_id first_dns first_proxy. - Keep the original, turn off the configuration in /root/egress/conf-after/, re-extract the records with
python3 /root/egress/collect.py, and write the numbers before and after turning off in /root/egress/after.txt. - Produce the audit submission summary in /root/egress/egress-report.json and /root/egress/egress-report.txt.
Notes
- All the materials are created under
/root/egress. The configurations are the threeconf/agent.conf,conf/collector.conf, andconf/plugins.json, the allowlist isallowlist.txt, and the records areproxy.log,dns.log, andchange.log. - Two tools are created together.
mkdata.pycreates the material, andcollect.pyre-extracts the records from the configuration inconf-after/and leavesproxy-after.loganddns-after.log. Neither uses random numbers, so you get the same result no matter how many times you run them. - Separate the fields of a table with tabs or spaces and do not put a header line. Write an empty field as a single hyphen.
- For ini configurations, pair
urlandenabledsection by section. If a section has noenabled, treat it as on (product manual section 4.3, an assumption of this lab). - If you read JSON with
jq, do not use.enabled // true. The alternative operator uses the right-hand side also when the value is exactlyfalse. First check for the presence of the key withhas("enabled"). - Response code 200 means it actually went out, 407 means the proxy demanded authentication, and 403 means the proxy blocked it. Do not lump the three together.
- Common mistakes: removing a destination hidden with a comment from the list, looking only at the proxy records and missing UDP communication, forcing a destination written as an address into either allowed or disallowed, and deleting records to make 0.
Create the material that will be the basis of the judgment, inside the network
Save and run /root/egress/mkdata.py, and also fetch and keep the re-collection tool /root/egress/collect.py.
It is an air-gapped network, so you cannot fetch the material from outside. The generation script is included as is in this step's solution, so save it and run it. Only if you use the script that does not use random numbers, as it is, does everyone get the same material and can check one another's judgments against each other. If you edit the files by hand, all the numbers in the later steps go off.
Extract every outside address from the configurations
Find every address pointing outside in the three configuration files and write it in /root/egress/endpoints.tsv as host port scheme state source.
A listen outside a section is a place to receive, not an outside address. List a destination hidden with a comment in the table as off, but do not remove it. In plugins.json, a missing enabled key and a value of false are different. Write only the file name for source.
Check against the allowlist and sort into three branches
Write it in /root/egress/classify.tsv as host port state verdict. verdict is one of allowed, denied, or unknown.
The allowlist is written by name and port. A destination whose address is written directly in the configuration has no way to be checked against that list, so it is neither allowed nor disallowed. That a private range makes it unlikely to have gone outside and that you confirmed it are different statements.
Count actual attempts in the proxy records by response
In /root/egress/attempts.tsv, write the attempts to disallowed destinations as host port attempts ok auth blocked.
ok is the count of 200, auth of 407, and blocked of 403, and attempts is the sum of the three. Do not put destinations that are in the allowlist in this table. If there is a destination that is not in the configuration but appears only in the records, that is also disallowed.
Find destinations that only had their name asked, with no connection
Write host queries nxdomain in /root/egress/dns-only.tsv, and in /root/egress/resolver.txt write the result of your own direct observation of this Pod's name resolution.
Communication that does not go through the proxy leaves nothing in the access records. A name that is in the query records but not in the access records is that trace. Leave out approved destinations. resolver.txt is three lines, nameserver, query, and status, and write status exactly as the string dig produced.
Link the three records by time and pinpoint since when
In /root/egress/timeline.tsv, write host change_id first_dns first_proxy for each disallowed destination. Leave missing fields as a single hyphen.
The targets are the destinations from step 4 and step 5 combined. If you look at which section of the configuration the item value in change.log points to, the changes and the destinations connect. If there is no change to pair with, it is a hyphen. Revealing destinations that arose outside configuration management is the use of this table.
Turn it off, re-collect, and show that it disappeared
In /root/egress/conf-after/, turn off the disallowed destinations, re-extract the records with python3 /root/egress/collect.py, and then write four numbers in /root/egress/after.txt.
The original configuration is evidence, so edit the copy. Do not delete items; only turn them off. For a feature that is truly needed, redirect it to a destination inside the allowlist instead of turning it off. You cannot make 0 by deleting the records by hand. The grader also checks that the approved destinations are still there.
Produce the audit submission summary
In /root/egress/egress-report.json and /root/egress/egress-report.txt, produce a summary that states the scope you confirmed and what you could not confirm together.
All the numbers come from the tables of the earlier steps. Put only destinations that could not be checked by name into unverified_hosts. direct_egress_checked and packet_capture_checked are things this material could not confirm, so write them honestly. The human-readable table must include one line for each destination that appeared only in the records.