def CISC_L2_000150(existingConfiguration): #Description: The Cisco switch must have Dynamic Address Resolution Protocol (ARP) Inspection (DAI) enabled on all user VLANs. test = "CISC_L2_000130" print_title(test) vlansList = defaultdict(list) commandToTest = ['ip arp inspection'] shouldExist = True exactMatch = False lineByLineComparison(existingConfiguration, commandToTest, shouldExist, test, exactMatch) for key, values in existingConfiguration.items(): for v in values: for x in v: currentLine = x if currentLine.startswith("ip arp inspection"): arpInspection = True tempString = currentLine.replace('ip arp inspection vlan ', '') arpInspectionVlans = tempString.split(',') vlansList = vlanParser(arpInspectionVlans) #collect current interface if currentLine.startswith("interface"): interface = currentLine if currentLine.startswith(" switchport access vlan "): filterString = currentLine.replace(' switchport access vlan ', '') splitString = filterString.split(',') vlansOnAccessInterface = vlanParser(splitString) if (set(vlansOnAccessInterface).issubset(set(vlansList)) == False): testResult = interface + " is an access interface with VLANs that are not being inspected by Dynamic ARP Inspection. Please remediate." resultDictionary[key][test].append(testResult)
def CISC_L2_000180(existingConfiguration): #Description: The Cisco switch must implement Rapid STP where VLANs span multiple switches with redundant links. test = "CISC_L2_000180" print_title(test) commandToTest = ['spanning-tree mode rapid-pvst'] shouldExist = True lineByLineComparison(existingConfiguration, commandToTest, shouldExist, test)
def CISC_L2_000160(existingConfiguration): test = "CISC_L2_000160" print_title(test) commandToTest = 'storm-control unicast' checkInterface(existingConfiguration, commandToTest, test) commandToTest = 'storm-control broadcast' checkInterface(existingConfiguration, commandToTest, test)
def CISC_L2_000110(existingConfiguration): #Description: The Cisco switch must have STP Loop Guard enabled. loopGuard = ['spanning-tree loopguard default'] shouldExist = True test = "CISC_L2_000110" print_title(test) lineByLineComparison(existingConfiguration, loopGuard, shouldExist, test)
def CISC_L2_000130(existingConfiguration): #Description: The Cisco switch must have DHCP snooping for all user VLANs to validate DHCP messages from untrusted sources test = "CISC_L2_000130" print_title(test) vlansList = defaultdict(list) commandToTest = ['ip dhcp snooping'] shouldExist = True exactMatch = False lineByLineComparison(existingConfiguration, commandToTest, shouldExist, test, exactMatch) for key, values in existingConfiguration.items(): for v in values: for x in v: currentLine = x if currentLine.startswith("ip dhcp snooping"): dhcpSnooping = True tempString = currentLine.replace('ip dhcp snooping vlan ', '') dhcpSnoopingVlans = tempString.split(',') vlansList = vlanParser(dhcpSnoopingVlans) #collect current interface if currentLine.startswith("interface"): interface = currentLine if currentLine.startswith(" switchport access vlan "): filterString = currentLine.replace(' switchport access vlan ', '') splitString = filterString.split(',') vlansOnAccessInterface = vlanParser(splitString) if (set(vlansOnAccessInterface).issubset(set(vlansList)) == False): testResult = interface + " is an access interface with VLANs that are not being snooped. Please remediate." resultDictionary[key][test].append(testResult)
def CISC_L2_000040(existingConfiguration): #Description: The Cisco switch must have STP Loop Guard enabled. qos = ['mls qos'] shouldExist = True test = "CISC_L2_000040" print_title(test) lineByLineComparison(existingConfiguration, qos, shouldExist, test)
def main(): # passphrase = getpass.getpass('Please enter CA key Passphrase') passphrase = 'fred' if os.path.isfile('./ssl/ca_key.pem') is False and os.path.isfile( './ssl/ca_cert.pem') is False: ca_key = cry.create_key() cry.save_key(ca_key, './ssl/ca_key.pem', passphrase) ca_csr = cry.create_csr(ca_key, country_name='GB', locality='London', organisation='Tactical Networks', common_name='CA.tactical-net.co.uk', is_ca=True) ca_cert = cry.create_cert(ca_key, csr=ca_csr, lifetime=3650, is_ca=True) cry.save_cert(ca_cert, './ssl/ca_cert.pem') else: ca_key = cry.load_key('./ssl/ca_key.pem', passphrase) ca_cert = cry.load_cert('./ssl/ca_cert.pem') print_title("Playbook to configure the network") nr = InitNornir(config_file='config.yaml', dry_run=False) # result = nr.run(task=disable_api, name='Disable API Task') # print_result(result, severity_level=logging.INFO) # nr = nr.filter(platform='nxos') result = nr.run(task=task_wrangler, name='Main Task Wrangler', ca_key=ca_key, ca_cert=ca_cert) print_result(result, severity_level=logging.INFO)
def CISC_L2_000020(existingConfiguration): #Description: Verify if the switch configuration has 802.1x authentication implemented for all access switch ports connecting to LAN outlets (i.e., RJ-45 wall plates) or devices not located in the telecom room, wiring closets, or equipment rooms. MAC Authentication Bypass (MAB) must be configured on those switch ports connected to devices that do not support an 802.1x supplicant. test = "CISC_L2_000020" print_title(test) hasBoth = 0 #this will keep a count per interface to see if both critieria are met for key, values in existingConfiguration.items(): for v in values: for current_line in v: interface = current_line hasBoth = 0 #reset counter for hasBoth if interface.startswith("interface"): #find the index position within the configuration to check for dot1x configuration index_element = v.index(interface) while v[index_element] != '!': #for the comparison we are using a leading because the config contains a space for indentation if v[index_element] == ' authentication port-control auto' or v[index_element] == ' dot1x pae authenticator': hasBoth += 1 #increment if a match is found if hasBoth == 2: break index_element += 1 #check the next line until reaching the end of interface configuration if hasBoth < 2: testResult = str(interface) + " is missing do1x configuration. Please remediate" resultDictionary[key][test].append(testResult) #Verify 802.1x configuration on the switch checkContent = [ 'aaa new-model', 'aaa group server radius', 'server name', 'aaa authentication do1x default group', 'dot1x system-auth-control' ] shouldExist = True lineByLineComparison(existingConfiguration, checkContent, shouldExist, test)
def CISC_L2_000220(existingConfiguration): #Description: The Cisco switch must not have the default VLAN assigned to any host-facing switch ports. test = "CISC_L2_000220" print_title(test) output = access_switches.run(task=networking.napalm_cli, commands=['show vlan brief']) configDictionary = defaultdict(list) #build a datastructure with host: result pairings for h, l in zip(hostKeys, listOfKeys): filtered_output = output[l].result someString = filtered_output['show vlan brief'] stringList = someString.split('\n') for x in stringList: if '1 default' in x: line = x tempString = " ".join(line.split()) stringList = tempString.split(' ') #if the last element in the stringList array is a description rather than #an interface we know that no interfaces are assigned here if stringList[-1] == 'active' or stringList[-1] == 'inactive': continue else: testResult = stringList[-1] + " on host " + h + " has access interfaces in the default VLAN. Please remediate." resultDictionary[h][test].append(testResult)
def CISC_L2_000230(existingConfiguration): #Description: The Cisco switch must have the default VLAN pruned from all trunk ports that do not require it. test = "CISC_L2_000230" print_title(test) output = access_switches.run(task=networking.napalm_cli, commands=['show int trunk']) configDictionary = defaultdict(list) #build a datastructure with host: result pairings for h, l in zip(hostKeys, listOfKeys): filtered_output = output[l].result someString = filtered_output['show int trunk'] stringList = someString.split('\n') for x in stringList: line = x if 'Vlans allowed on trunk' in line: index_element = stringList.index(line) index_element +=1 #iterate to the next line of the output which will contain ports and their allowed vlans on trunk while stringList[index_element].startswith('Port') == False: currentLine = stringList[index_element] interface = currentLine.split(' ') #grab the interface name if('1,') in currentLine or ('1-') in currentLine: #check the current line for VLAN1 in the interface trunking testResult = "VLAN1 is not being pruned from " + interface[0] + " on host " + h + ". Please remediate." resultDictionary[h][test].append(testResult) index_element +=1
def CISC_L2_000170(existingConfiguration): #Description: The Cisco switch must have IGMP or MLD Snooping configured on all VLANs. test = "CISC_L2_000170" print_title(test) commandToTest = ['no ip igmp snooping'] shouldExist = False exactMatch = False lineByLineComparison(existingConfiguration, commandToTest, shouldExist, test, exactMatch)
def main() -> None: clean_up = "rm -r ospfdiff ospf-current" os.system(clean_up) os.system(clear_command) nr = InitNornir(config_file="config.yaml") output = nr.run(task=clean_ospf) print_title("REVERSING OSPF CONFIGURATION BACK INTO DESIRED STATE") print_result(output)
def CISC_L2_000270(existingConfiguration): #Description: The Cisco switch must not have any switchports assigned to the native VLAN. test = "CISC_L2_000270" print_title(test) checkContent = ['native vlan'] shouldExist = False exactMatch = False lineByLineComparison(existingConfiguration, checkContent, shouldExist, test, exactMatch)
def save_config(self, task): config, site = self.render_config(task) print_title(f"Saving configuration to: output/{site}.conf.") try: with open(f'output/{site}.conf', "a") as file: file.write(config) file.close() print("[+] Done") except Exception as e: print(f"[-] An Error occured: {e}")
def process_tasks(task): if task.failed: print_result(task) print("Exiting script before we break anything else!") sys.exit(1) else: print_title('Processing results') for host in dc1.inventory.hosts: host_lldp = cumulus_lldp(dc1.inventory.hosts[host]['lldp'], host) print(f"Task {task.name} completed successfully!")
def main() -> None: clear_command = "clear" clean_up = "rm -r vlandiff vlan-current" os.system(clean_up) os.system(clear_command) nr = InitNornir(config_file="config.yaml") targets = nr.filter(device="switch") result = targets.run(task=deploy_vlan) output = targets.run(task=show_vlan) print_title("REVERSING VLAN CONFIGURATION BACK INTO DESIRED STATE") print_result(output)
def render_config(self, task): site = task.host["site"] config_data = self.open_config(task) templateLoader = jinja2.FileSystemLoader(searchpath="templates/") templateEnv = jinja2.Environment(loader=templateLoader) t = self.template template = templateEnv.get_template(t) print_title(f"Rendering configuration: {self.template} on {site}.") config = template.render(config_data) print(config) return config, site
def deploy_config(self, task): config, site = self.render_config(task) print_title(f"Configuring {self.template} on {site}.") try: task.run(task=networking.napalm_configure, name="Loading Configuration on the device", replace=False, configuration=config) print("[+] Done") except Exception as e: print(f"[-] An Error occured: {e}")
def CISC_L2_000190(existingConfiguration): #Description: The Cisco switch must enable Unidirectional Link Detection (UDLD) to protect against one-way connections. test = "CISC_L2_000190" print_title(test) commandToTest = ['udld enable'] shouldExist = True lineByLineComparison(existingConfiguration, commandToTest, shouldExist, test) #This will check on a per interface basis. We should consider modifying line by line to a boolean return... commandToTest = 'udld port' checkInterface(existingConfiguration, commandToTest, test)
def CISC_L2_000210(existingConfiguration): #Description: The Cisco switch must have all disabled switch ports assigned to an unused VLAN. test = "CISC_L2_000210" print_title(test) output = access_switches.run(task=networking.napalm_cli, commands=['show interfaces switchport']) activeVlansOnHost = defaultdict(list) configDictionary = defaultdict(list) #Setting a flag to determine if TrunkAll is set. We need at least one VLAN disabled/not trunking to have an attempt at passing this rule trunkAll = False c = set() #build a datastructure with host: result pairings for l in hostKeys: filtered_output = output[l].result someString = filtered_output['show interfaces switchport'] stringList = someString.split('\n') for x in stringList: activeVlans = [] if 'Name: ' in x: currentInterface = x if 'Trunking VLANs Enabled: ALL' in x: testResult = "Interface" + currentInterface + " on host " + l + " is trunking ALL VLANs. Please disable at least one VLAN." trunkAll = True resultDictionary[l][test].append(testResult) break if 'Trunking VLANs Enabled: ' in x and trunkAll == False: tempString = x.replace('Trunking VLANs Enabled: ', '') numbers = tempString.split(',') activeVlans = vlanParser(numbers) activeVlansOnHost[l] += activeVlans for key, values in existingConfiguration.items(): for v in values: for current_line in v: interface = current_line if interface.startswith("interface"): index_element = v.index(interface) interfaceConfigurationBlock = [] vlansOnInterfaceConfiguration = [] while v[index_element] != '!': currentLine = v[index_element] interfaceConfigurationBlock.append(currentLine) if 'switchport access vlan' in currentLine: temporaryString = currentLine.replace(' switchport access vlan ', '') remainingVlans = temporaryString.split(',') vlansOnInterfaceConfiguration = vlanParser(remainingVlans) index_element += 1 if ' switchport mode access' in interfaceConfigurationBlock and ' shutdown' in interfaceConfigurationBlock and (set(activeVlansOnHost[key]).intersection(set(vlansOnInterfaceConfiguration)) != c): testResult = interface + " is shutdown in a VLAN that is in use. Please remediate" resultDictionary[key][test].append(testResult)
def main(): nr = InitNornir(config_file="config.yml") ios = nr.inventory.groups['cisco-ios']['img'] # Task: SCP file to device print_title(f'Sending {ios} via SCP') output = nr.run(scp_file) print_result(output) # Task: Check that file was installed print_title(f'Verifying {ios} was installed') output = nr.run(check_file_exists) print_result(output) # Task: Set boot variable to new ios img print_title('Setting boot variable') output = nr.run(set_boot_var) print_result(output) # Task: Reload devices to install new img print_title('Reloading devices') output = nr.run(reload) print_result(output) print('Reloading...')
def CISC_L2_000240(existingConfiguration): #Description: The Cisco switch must not use the default VLAN for management traffic. test = "CISC_L2_000240" print_title(test) for key, values in existingConfiguration.items(): for v in values: for current_line in v: interface = current_line if interface == 'interface Vlan1': #find the index position within the configuration to check for dot1x configuration index_element = v.index(interface) index_element += 1 if 'Management' or 'MGMT' in v[index_element]: testResult = "The management VLAN is using VLAN1. Please remediate." resultDictionary[key][test].append(testResult)
def main(): nr = InitNornir( config_file="nornir_config.yaml", core={ 'num_workers':10 } ) # register the ncclient connection plugin so that host-connections have access to it Connections.register("ncclient", Ncclient) nr2 = nr.filter(name='sr1.lab') results = nr.run(task=validate_intent, intent_dir="intents") # results = nr2.run(task=validate_intent, intent_dir="intents") print_title("Runbook to push intent") print_result(results, vars = "diff")
def CISC_L2_000030(existingConfiguration): #Description: The Cisco switch must authenticate all VLAN Trunk Protocol (VTP) messages with a hash function using the most secured cryptographic algorithm available. test = "CISC_L2_000030" print_title(test) output = access_switches.run(task=networking.napalm_cli, commands=['show vtp password']) configDictionary = defaultdict(list) #build a datastructure with host: result pairings for h, l in zip(hostKeys, listOfKeys): filtered_output = output[l].result someString = filtered_output['show vtp password'] if 'not configured' in someString: testResult = "Host " + h + " does not have a VTP password. Please set one." resultDictionary[h][test].append(testResult) configDictionary[h].append(someString) filtered_output = None someString = None
def CISC_L2_000200(existingConfiguration): #Description: The Cisco switch must have all trunk links enabled statically. test = "CISC_L2_000200" print_title(test) output = access_switches.run(task=networking.napalm_cli, commands=['show interfaces switchport']) configDictionary = defaultdict(list) #build a datastructure with host: result pairings for h, l in zip(hostKeys, listOfKeys): filtered_output = output[l].result someString = filtered_output['show interfaces switchport'] stringList = someString.split('\n') for x in stringList: if 'Name: ' in x: currentInterface = x if 'Negotiation of Trunking: On' in x: testResult = "Interface" + currentInterface + " on host " + h + " is using auto negotiation of trunking. Please remediate." resultDictionary[h][test].append(testResult)
def CISC_L2_000260(existingConfiguration): #Description: The Cisco switch must have the native VLAN assigned to an ID other than the default VLAN for all 802.1q trunk links. test = "CISC_L2_000260" print_title(test) output = access_switches.run(task=networking.napalm_cli, commands=['show interfaces switchport']) configDictionary = defaultdict(list) #build a datastructure with host: result pairings for h, l in zip(hostKeys, listOfKeys): filtered_output = output[l].result someString = filtered_output['show interfaces switchport'] stringList = someString.split('\n') for x in stringList: if 'Name: ' in x: tempString = x currentInterface = tempString.replace("Name: ", '') if 'Trunking Native Mode VLAN: 1 (default)' in x: testResult = "Interface " + currentInterface + " on host " + h + " is trunking the native VLAN. Please remediate." resultDictionary[h][test].append(testResult)
def main(): # Create a Nornir object nr = InitNornir(core={"num_workers": 100}, inventory={ "plugin": "nornir.plugins.inventory.simple.SimpleInventory", "options": { "host_file": "inventory/hosts.yaml", "group_file": "inventory/groups.yaml" } }) # Run Nornir task (here getting the ARP table of the devices) result = nr.run(task=networking.napalm_get, name=" ARP table ", getters=["arp_table"]) # Display the result print_title("Display ARP table of the network devices") print_result(result)
def main(): nr = InitNornir(config_file="nornir_config.yaml", core={'num_workers': 10}) # register the ncclient connection plugin so that host-connections have access to it Connections.register("ncclient", Ncclient) nr2 = nr.filter(name='sr1.lab') print_title("Loading Service Intents") results = nr.run(task=load_service_intents, name="Load Service Intents", directory="intent2/services/eline/vars") print_result(results, vars="stdout") print_title("Updating inventory host data (partial)") resources_to_load = set() for _, host in nr.inventory.hosts.items(): resources_needed = host.get("rsc_to_populate", None) if resources_needed: for rsc in resources_needed: resources_to_load.add(rsc) results = nr.run(task=download_configs, name="Load relevant configs into Inventory", resources=resources_to_load) print_result(results, vars="stdout") print_title("Ensuring Service Intents") results = nr.run(task=ensure_services, intent_dir="intent2/services") print_result(results, vars="diff")
def CISC_L2_000010(existingConfiguration): #Description: The Cisco switch must be configured to disable non-essential capabilities. test = "CISC_L2_000010" checkContent = [ 'boot network', 'ip boot server', 'ip bootp server', 'ip dns server', 'ip identd', 'ip finger', 'ip http server', 'ip rcmd rcp-enable', 'ip rcmd rsh-enable service config', 'service finger' 'service tcp-small-servers', 'service udp-small-servers', 'service pad' ] shouldExist = False print_title(test) lineByLineComparison(existingConfiguration, checkContent, shouldExist, test)
def main(): nr = InitNornir(core={"num_workers": 100}, inventory={ "plugin": "nornir.plugins.inventory.simple.SimpleInventory", "options": { "host_file": "inventory/hosts.yaml", "group_file": "inventory/groups.yaml" } }) hosts = nr.filter(type="network_device") result = hosts.run(task=networking.napalm_get, name=" Get facts ", getters=["facts", "interfaces", "environment"]) print_title("Display info about network devices") print_result(result) print(result["device1"][0].result.get("facts").get("os_version"))