""" Get details about packets blocked by ufw (uses journald) """ import datetime import re from systemd import journal from logparse import config from logparse.formatting import * from logparse.load_parsers import Parser from logparse.util import resolve PROTOCOLS = ["TCP", "UDP", "UDP-Lite", "ICMP", "ICMPv6", "AH", "SCTP", "MH"] class Packet(): """ Class to hold variables for each packet. Also parses incoming log messages on object initialisation. """ def __init__(self, msg): """ Determine fields in log message. If any of the fields are missing the log is considered malformed and is discarded. Also the protocol can be specified as either an integer or a string - see `man ufw`. """ try: self.inif, self.outif, self.mac, self.src, self.dst, self.len, \ self.proto, self.spt, self.dpt = \ re.search(r"IN=(?P\w*).*OUT=(?P\w*).*" "MAC=(?P\S*).*SRC=(?P\S*).*DST=(?P\S*)" ".*LEN=(?P\d*).*PROTO=(?P\S*)" "(?:\sSPT=(?P\d*))?(?:\sDPT=(?P\d*))?", msg ).groupdict().values() if self.proto and self.proto.isdigit(): self.proto = PROTOCOLS[int(self.proto)-1] except Exception as e: logger.warning("Malformed packet log: {0}. Error message: {1}" .format(msg, str(e))) return None class UfwJournald(Parser): def __init__(self): super().__init__() self.name = "ufw" self.info = "Get details about packets blocked by ufw" def parse_log(self): logger.debug("Starting ufw section") section = Section("ufw") # Find applicable log entries j = journal.Reader() j.this_machine() j.add_match(_TRANSPORT='kernel') j.add_match(PRIORITY=4) j.seek_realtime(section.period.startdate) logger.debug("Searching for messages") blocked_packets = [Packet(entry["MESSAGE"]) for entry in j if "MESSAGE" in entry and "UFW BLOCK" in entry["MESSAGE"]] # Parse messages logger.debug("Parsing messages") inbound_interfaces = [] outbound_interfaces = [] n_inbound = n_outbond = 0 src_ips = [] dst_ips = [] src_ports = [] dst_ports = [] protocols = {'UDP': 0, 'TCP': 0} src_macs = [] for pkt in blocked_packets: if pkt.inif: inbound_interfaces.append(pkt.inif) elif pkt.outif: outbound_interfaces.append(pkt.outif) if pkt.src: src_ips.append(resolve(pkt.src, config.prefs.get("ufw", "ufw-resolve-domains"))) if pkt.dst: dst_ips.append(resolve(pkt.dst, config.prefs.get("ufw", "ufw-resolve-domains"))) if pkt.spt: src_ports.append(pkt.spt) if pkt.dpt: dst_ports.append(pkt.dpt) if pkt.proto: protocols[pkt.proto] += 1 # Format data objects section.append_data(Data(subtitle="{} blocked ({} UDP, {} TCP)".format( plural("packet", len(blocked_packets)), protocols['UDP'], protocols['TCP']))) src_port_data = Data(items=src_ports) src_port_data.orderbyfreq() src_port_data.subtitle = plural("source port", len(src_port_data.items)) src_port_data.truncl(config.prefs.getint("logparse", "maxlist")) section.append_data(src_port_data) dst_port_data= Data(items=dst_ports) dst_port_data.orderbyfreq() dst_port_data.subtitle = plural("destination port", len(dst_port_data.items)) dst_port_data.truncl(config.prefs.getint("logparse", "maxlist")) section.append_data(dst_port_data) src_ips_data= Data(items=src_ips) src_ips_data.orderbyfreq() src_ips_data.subtitle = plural("source IP", len(src_ips_data.items)) src_ips_data.truncl(config.prefs.getint("logparse", "maxlist")) section.append_data(src_ips_data) dst_ips_data= Data(items=dst_ips) dst_ips_data.orderbyfreq() dst_ips_data.subtitle = plural("destination IP", len(dst_ips_data.items)) dst_ips_data.truncl(config.prefs.getint("logparse", "maxlist")) section.append_data(dst_ips_data) logger.info("Finished ufw section") return section def check_dependencies(self): """ Basic dependency check to determine if there are any logs to parse """ ufw_cmdline = "ufw --version" if self._check_dependency_command(ufw_cmdline)[0] != 0: return (False, ["ufw"]) else: return (True, None)