Rules as data, required and forbidden lines, whole-line matching
Check compliance rules
Lesson 15 of 20 · 15 min · Module 4: Backups, drift and compliance
Security teams write standards: central time and logging, encrypted passwords, no Telnet, no default SNMP community. Turn the standard into data and check every configuration against it.
Start this lesson →Free. No installation: you write and run the code in your browser, and it is checked automatically.
By the end, you’ll be able to…
- Keep a standard as data
- Match whole lines, not pieces of text
- Report missing and forbidden lines
The idea
Keep the rules per platform as two lists: required lines that must be present and forbidden lines that must not be. Compare whole lines: put the stripped lines of the configuration in a set and look each rule up. Testing with in on the whole text is not enough, because a short rule can match part of a longer line. Compliance checks usually run on the backups, so they need no login at all.
config = "line vty 0 4\n transport input telnet ssh\n"
lines = {line.strip() for line in config.splitlines()}
print("transport input telnet" in config)
print("transport input telnet" in lines)
01Configuration lines as a set
strip() removes the indentation inside sections, so " transport input ssh" and "transport input ssh" become the same line.
text = open("backups/sw2.cfg").read()
lines = {line.strip() for line in text.splitlines()}
print("transport input telnet ssh" in lines)
print("ip http server" in lines)True
True02Pieces of text mislead
The forbidden rule transport input telnet is part of the longer line on sw2, so in on the text says yes. The set of whole lines says no.
print("transport input telnet" in text)
print("transport input telnet" in lines)True
False03Required and forbidden
Two list comprehensions produce the report, in the order of the rules.
required = ["no ip http server", "service password-encryption"]
forbidden = ["transport input telnet ssh"]
found = [f"missing: {r}" for r in required if r not in lines]
found += [f"forbidden: {r}" for r in forbidden if r in lines]
print(found)['missing: no ip http server', 'forbidden: transport input telnet ssh']Your turn
backups/ against RULES for its platform (from devices.csv). Build violations: device name → the list of "missing: <line>" for required lines that are absent, followed by "forbidden: <line>" for forbidden lines that are present, both in rule order. Compare whole lines, ignoring spaces at the ends. Skip devices that have no backup.What the checks look for
- Compliant devices have no violations
- sw1: encryption, NTP and the public community
- sw2: HTTP server, log server and Telnet
- sw3: Huawei NTP servers and Telnet
- Devices without a backup are skipped
Take this with you
Rules are data; checking a rule is a set lookup on whole lines.
Go a little further
Add a third kind of rule, required_any: at least one of several lines must be present, for example transport input ssh or no transport input.