def translate_addr_rule(addr, addr_type, addr_table, port_op, port_from, port_to, proto, addr_iface, af): """Parses fields given in the pfweb form to a pf.PFRuleAddr object""" pfaddr = False if addr_type == "addrmask" and af != socket.AF_UNSPEC: # Validate IP address pfaddr = translate_addrmask(af, addr) elif addr_type == "table": # Set addr to a table if not addr_table: return "Table cannot be empty" pfaddr = pf.PFAddr("<{}>".format(addr_table)) elif addr_type == "dynif": # Set addr to an interface if not addr_iface: return "Interface cannot be empty" pfaddr = pf.PFAddr("({})".format(addr_iface), af) # Do not set if ANY or proto is ICMP port = False if int( port_op ) != pf.PF_OP_NONE and proto != socket.IPPROTO_ICMP and proto != socket.IPPROTO_ICMPV6: # Confirm port op if int(port_op) not in PFWEB_PORT_OPS: return "Invalid port op" # port from pfport_from = 0 try: # Confirm input is a number if port_from != '': pfport_from = int(port_from) except ValueError: return "Invalid port number" pfport_to = 0 # Set range if int(port_op) == pf.PF_OP_RRG or int(port_op) == pf.PF_OP_IRG or int( port_op) == pf.PF_OP_XRG: # Port to try: if port_to != '': pfport_to = int(port_to) except ValueError: return "Invalid port number" port = pf.PFPort((pfport_from, pfport_to), proto, int(port_op)) # Create and set the PFRuleAddr rule_addr = pf.PFRuleAddr() if pfaddr: rule_addr.addr = pfaddr if port: rule_addr.port = port return rule_addr
def translate_pool_rule(trans_type, addr, addr_type, addr_table, port_from, port_to, proto, addr_iface, af): """Parses fields given in the pfweb form to a pf.PFPool object""" pfaddr = False if addr_type == 'addrmask': pfaddr = translate_addrmask(af, addr) elif addr_type == 'table': if not addr_table: return "Table cannot be empty" pfaddr = pf.PFAddr("<{}>".format(addr_table)) elif addr_type == 'dynif': if trans_type.lower() == 'rdr': return "Cannot RDR to an interface" if not addr_iface: return "Interface cannot be empty" # Set PFAddr to interface and IPv4 pfaddr = pf.PFAddr("({})".format(addr_iface), af) pool_id = pf.PF_POOL_NAT port = False if trans_type.lower() == 'rdr' and (proto == socket.IPPROTO_TCP or proto == socket.IPPROTO_UDP): # Set ports to 0 if they were left blank if port_from == '': port_from = 0 port_to = 0 if port_to == '': port_to = 0 pool_id = pf.PF_POOL_RDR try: port = pf.PFPort((int(port_from), int(port_to))) except ValueError: # The user didn't give us a valid number return "Invalid port number" elif trans_type.lower() == 'rdr' and not (proto == socket.IPPROTO_TCP or proto == socket.IPPROTO_UDP): return "TCP or UDP must be used for RDR" pool = pf.PFPool(pool_id, pfaddr) if port: pool.proxy_port = port return pool
def test_load_ruleset(self): iface = pf.PFAddr(type=pf.PF_ADDR_DYNIFTL, ifname=self.testif) tables = [pf.PFTable("web_srv", "10.0.1.20", "10.0.1.21", "10.0.1.22")] rules = [ # match out on $ifname inet from !($ifname) to any nat-to ($ifname) pf.PFRule(action=pf.PF_MATCH, direction=pf.PF_OUT, ifname=self.testif, af=AF_INET, src=pf.PFRuleAddr(iface, neg=True), nat=pf.PFPool(pf.PF_POOL_NAT, iface)), # pass out quick pf.PFRule(action=pf.PF_PASS, direction=pf.PF_OUT, quick=True, flags="S", flagset="SA", keep_state=pf.PF_STATE_NORMAL), # anchor "test_anchor" pf.PFRuleset("test_anchor"), # pass in on $ifname inet proto tcp from any to $ifname port ssh pf.PFRule(action=pf.PF_PASS, direction=pf.PF_IN, ifname=self.testif, af=AF_INET, proto=IPPROTO_TCP, dst=pf.PFRuleAddr(iface, pf.PFPort("ssh", IPPROTO_TCP)), flags="S", flagset="SA", keep_state=pf.PF_STATE_NORMAL), # pass in on $ifname inet proto tcp to $ifname port www \ # rdr-to <web_srv> round-robin sticky-address pf.PFRule(action=pf.PF_PASS, direction=pf.PF_IN, ifname=self.testif, af=AF_INET, proto=IPPROTO_TCP, dst=pf.PFRuleAddr(iface, pf.PFPort("www", IPPROTO_TCP)), flags="S", flagset="SA", keep_state=pf.PF_STATE_NORMAL, rdr=pf.PFPool(pf.PF_POOL_RDR, pf.PFAddr("<web_srv>"), opts=(pf.PF_POOL_ROUNDROBIN| pf.PF_POOL_STICKYADDR))), # pass out on $ifname inet proto tcp to port 80 \ # divert-packet port 700 pf.PFRule(action=pf.PF_PASS, direction=pf.PF_OUT, ifname=self.testif, af=AF_INET, proto=IPPROTO_TCP, dst=pf.PFRuleAddr(port=pf.PFPort("www", IPPROTO_TCP)), divert=pf.PFDivert(pf.PF_DIVERT_PACKET, port=700)), # pass in inet proto icmp all icmp-type echoreq max-pkt-rate 100/10 pf.PFRule(action=pf.PF_PASS, direction=pf.PF_IN, af=AF_INET, proto=IPPROTO_ICMP, type=pf.ICMP_ECHO+1, keep_state=pf.PF_STATE_NORMAL, pktrate=pf.PFThreshold(100, 10))] rules[2].append( pf.PFTable("spammers", flags=pf.PFR_TFLAG_PERSIST), # pass in on $ifname inet proto tcp from ! <spammers> \ # to $ifname port 25 rdr-to 10.0.1.23 pf.PFRule(action=pf.PF_PASS, direction=pf.PF_IN, ifname=self.testif, af=AF_INET, proto=IPPROTO_TCP, src=pf.PFRuleAddr(pf.PFAddr("<spammers>"), neg=True), dst=pf.PFRuleAddr(iface, pf.PFPort(25, IPPROTO_TCP)), flags="S", flagset="SA", keep_state=pf.PF_STATE_NORMAL, rdr=pf.PFPool(pf.PF_POOL_RDR, pf.PFAddr("10.0.1.23")))) self.pf.clear_rules() rs = pf.PFRuleset() rs.append(*tables) rs.append(*rules) self.pf.load_ruleset(rs) self.assertEqual(len(self.pf.get_ruleset().rules), len(rules))
def translate_rule(pfilter, **fields): """Parse form fields into a pf.PFRule""" # Load existing or create new rule if fields['id'] or fields['id'] == 0: ruleset = pfilter.get_ruleset() rule = ruleset.rules[fields['id']] else: rule = pf.PFRule() # Set action attribute if fields['action'] == 'pass': rule.action = pf.PF_PASS elif fields['action'] == 'block': rule.action = pf.PF_DROP rule.rule_flag = pf.PFRULE_DROP | 0 elif fields['action'] == 'reject': rule.action = pf.PF_DROP rule.rule_flag = pf.PFRULE_RETURN | 0 elif fields['action'] == 'match': rule.action = pf.PF_MATCH else: return "Action is not recognized" # Set direction attribute if fields['direction'] == 'in': rule.direction = pf.PF_IN elif fields['direction'] == 'out': rule.direction = pf.PF_OUT elif fields['direction'] == 'both': rule.direction = pf.PF_INOUT else: return "Direction is not recognized" # Set interface attribute if fields['iface'] in get_ifaces(pfilter): rule.ifname = fields['iface'] else: return "Unknown interface specified" # Set address family attribute if fields['af'] == '*': rule.af = socket.AF_UNSPEC elif fields['af'] == 'IPv4': rule.af = socket.AF_INET elif fields['af'] == 'IPv6': rule.af = socket.AF_INET6 else: return "Unknown address family type" # Set protocol attribute if fields['proto'] == '*': rule.proto = socket.IPPROTO_IP elif fields['proto'] == 'TCP': rule.proto = socket.IPPROTO_TCP elif fields['proto'] == 'UDP': rule.proto = socket.IPPROTO_UDP elif fields['proto'] == 'ICMP' and fields['af'] == 'IPv4': rule.proto = socket.IPPROTO_ICMP elif fields['proto'] == 'ICMP' and fields['af'] == 'IPv6': rule.proto = socket.IPPROTO_ICMPV6 else: return "Protocol is not supported" # ICMP Type if fields['proto'] == "ICMP": # Use ICMP or ICMP6 depending on AF if rule.af == socket.AF_INET: if fields['icmptype'] == 'any': rule.type = 0 elif int(fields['icmptype']) in PFWEB_ICMP_TYPES: rule.type = int(fields['icmptype']) + 1 else: return "Invalid ICMP Type" elif rule.af == socket.AF_INET6: if fields['icmp6type'] == 'any': rule.type = 0 elif int(fields['icmp6type']) in PFWEB_ICMP6_TYPES: rule.type = int(fields['icmp6type']) + 1 else: return "Invalid ICMP Type" else: return "Must specifiy IPv4 or IPv6 when using ICMP" # Source Address Rule rule.src = translate_addr_rule(fields.get('src_addr'), fields.get('src_addr_type'), fields.get('src_addr_table'), fields.get('src_port_op', pf.PF_OP_NONE), fields.get('src_port_from', 0), fields.get('src_port_to', 0), rule.proto, fields.get('src_addr_iface'), rule.af) # Destination Address Rule rule.dst = translate_addr_rule(fields.get('dst_addr'), fields.get('dst_addr_type'), fields.get('dst_addr_table'), fields.get('dst_port_op', pf.PF_OP_NONE), fields.get('dst_port_from', 0), fields.get('dst_port_to', 0), rule.proto, fields.get('dst_addr_iface'), rule.af) # Set any translation used NAT or RDR if fields.get('trans_type', 'none') != 'none' and ( rule.af == socket.AF_INET or rule.af == socket.AF_INET6): pool = translate_pool_rule(fields.get('trans_type'), fields.get('trans_addr'), fields.get('trans_addr_type'), fields.get('trans_addr_table'), fields.get('trans_port_from'), fields.get('trans_port_to'), rule.proto, fields.get('trans_addr_iface'), rule.af) if fields['trans_type'].lower() == 'rdr': rule.rdr = pool rule.nat.addr.type = pf.PF_ADDR_NONE else: # Enable static port option if 'nat_static_port' in fields: pool.proxy_port = pf.PFPort((0, 0)) rule.nat = pool rule.rdr.addr.type = pf.PF_ADDR_NONE elif fields.get('trans_type', 'none') != 'none': return "Must specify IPv4 or IPv6 with translation" else: # Translation is disabled rule.rdr.addr.type = pf.PF_ADDR_NONE rule.nat.addr.type = pf.PF_ADDR_NONE # Log checkbox if 'log' in fields: rule.log = pf.PF_LOG else: rule.log = 0 # Quick checkbox if 'quick' in fields: rule.quick = True else: rule.quick = False # Keep state checkbox if 'keep_state' in fields: rule.keep_state = pf.PF_STATE_NORMAL else: rule.keep_state = 0 if 'label' in fields: rule.label = fields['label'] return rule
def states(): """Show all contents of the state table and allow a state to be removed""" if request.method == 'POST': # Remove individual state if request.form.get('action') == 'remove': # Make sure correct parameters were sent if request.form.get('src') and request.form.get('dst'): if '[' in request.form.get( 'src') or '.' not in request.form.get('src'): # Handle IPv6 addresses src_addr_port = request.form.get('src').split('[') dst_addr_port = request.form.get('dst').split('[') # Make sure there was a port set if len(src_addr_port) == 2: src_addr_port[1] = src_addr_port[1].split(']')[0] else: src_addr_port.append(0) if len(dst_addr_port) == 2: dst_addr_port[1] = dst_addr_port[1].split(']')[0] else: dst_addr_port.append(0) else: # IPv4 address and port src_addr_port = request.form.get('src').split(':') dst_addr_port = request.form.get('dst').split(':') # Create PFRuleAddr object from address and ports src_addr = pf.PFRuleAddr( pf.PFAddr(src_addr_port[0]), pf.PFPort(src_addr_port[1], 0, pf.PF_OP_EQ)) dst_addr = pf.PFRuleAddr( pf.PFAddr(dst_addr_port[0]), pf.PFPort(dst_addr_port[1], 0, pf.PF_OP_EQ)) packetfilter.kill_states(src=src_addr, dst=dst_addr) # Return a JSON object of just the src and dst we removed return jsonify({ 'src': "{}".format(request.form.get('src')), 'dst': "{}".format(request.form.get('dst')) }) else: # Return a simple 400 response when src or dst were not provided message = {'status': 400, 'message': 'Invalid parameters'} resp = jsonify(message) resp.status_code = 400 return resp else: # Return a simple 400 response the wrong action provided message = {'status': 400, 'message': 'Unknown action'} resp = jsonify(message) resp.status_code = 400 return resp states = list() for state in packetfilter.get_states(): # Set direction for src and dst (src, dst) = (1, 0) if state.direction == pf.PF_OUT else (0, 1) # Set the source address and port. Only set port if it is not 0 src_line = "{}".format(state.nk.addr[src]) if str(state.nk.port[src]): src_line += (":{}" if state.af == socket.AF_INET else "[{}]").format(state.nk.port[src]) # Show and NAT (or rdr) address if (state.nk.addr[src] != state.sk.addr[src] or state.nk.port[src] != state.sk.port[src]): src_line += " ({}".format(state.sk.addr[src]) if str(state.sk.port[src]): src_line += (":{})" if state.af == socket.AF_INET else "[{}])").format(state.sk.port[src]) # Repeat for destination dst_line = "{}".format(state.nk.addr[dst]) if str(state.nk.port[dst]): dst_line += (":{}" if state.af == socket.AF_INET else "[{}]").format(state.nk.port[dst]) if (state.nk.addr[dst] != state.sk.addr[dst] or state.nk.port[dst] != state.sk.port[dst]): dst_line += " ({}".format(state.sk.addr[dst]) if str(state.sk.port[dst]): dst_line += (":{})" if state.af == socket.AF_INET else "[{}])").format(state.sk.port[dst]) state_desc = "" if state.proto == socket.IPPROTO_TCP: state_desc = "{}:{}".format(PFWEB_TCP_STATES[state.src.state], PFWEB_TCP_STATES[state.dst.state]) elif state.proto == socket.IPPROTO_UDP: state_desc = "{}:{}".format(PFWEB_UDP_STATES[state.src.state], PFWEB_UDP_STATES[state.dst.state]) else: state_desc = "{}:{}".format(PFWEB_OTHER_STATES[state.src.state], PFWEB_OTHER_STATES[state.dst.state]) state_struct = { 'ifname': state.ifname, 'proto': PFWEB_IPPROTO[state.proto], 'src': src_line, 'dst': dst_line, 'state': state_desc, 'packets': [ int(sum(state.packets)), "TX: {}<br/>RX: {}".format( sizeof_fmt(state.packets[0], num_type='int'), sizeof_fmt(state.packets[1], num_type='int')) ], 'bytes': [ int(sum(state.bytes)), "TX: {}<br/>RX: {}".format(sizeof_fmt(state.bytes[0]), sizeof_fmt(state.bytes[1])) ], 'expires': [int(state.expire), timedelta(seconds=state.expire)] } states.append(state_struct) return render_template('states.html', logged_in=flask_login.current_user.get_id(), status_tab='active', states=states)
def xml_to_pfrule(data): print "starting translation" print data xml_datas = [] xml_rules = xmltodict.parse( data, dict_constructor=dict )["mspl-set"]["it-resource"]["configuration"]["rule"] if isinstance(xml_rules, list): for xml_rule in xml_rules: xml_datas.append(xml_rule) else: xml_datas.append(xml_rules) rate_limit = None rules = pf.PFRuleset() for xml_data in xml_datas: print xml_data rule = pf.PFRule() if settings.quick_rules == "True": rule.quick = True Addrs = None Addrd = None Ports = None Portd = None xml_src = getValIfKeyExists( xml_data["condition"]["packet-filter-condition"], "source-address") if xml_src is not None: Addrs = pf.PFAddr(xml_src) xml_dst = getValIfKeyExists( xml_data["condition"]["packet-filter-condition"], "destination-address") if xml_dst is not None: Addrd = pf.PFAddr(xml_dst) xml_proto = getValIfKeyExists( xml_data["condition"]["packet-filter-condition"], "protocol") if xml_proto is not None: if xml_proto.lower() == "tcp": rule.proto = socket.IPPROTO_TCP elif xml_proto.lower() == "udp": rule.proto = socket.IPPROTO_UDP xml_direction = getValIfKeyExists( xml_data["condition"]["packet-filter-condition"], "direction") if xml_direction is not None: if xml_direction.lower() == "inbound": rule.direction = pf.PF_IN elif xml_direction.lower() == "outbound": rule.direction = pf.PF_OUT xml_interface = getValIfKeyExists( xml_data["condition"]["packet-filter-condition"], "interface") if xml_interface is not None: rule.ifname = xml_interface.replace("eth", "vtnet") xml_sport = getValIfKeyExists( xml_data["condition"]["packet-filter-condition"], "source-port") xml_dport = getValIfKeyExists( xml_data["condition"]["packet-filter-condition"], "destination-port") # Retrieve default target from MSPL default_target = getValIfKeyExists(xml_data, "action").upper() if default_target is not None: if default_target == "ACCEPT": rule.action = pf.PF_PASS rule.keep_state = pf.PF_STATE_NORMAL elif default_target == "DROP": rule.action = pf.PF_DROP elif default_target == "REJECT": rule.action = pf.PF_DROP rule.rule_flag = pf.PFRULE_RETURN elif default_target == "SCRUB": rule.action = pf.PF_SCRUB # In case of packets/sec rate limit: if getValIfKeyExists( xml_data["condition"], "traffic-flow-condition") is not None: # It is a number of allowed packets or bits per unit of time # (seconds, minutes, hours or days). # -m limit --limit <rate-limit> --limit-burst <limit-burst> \ # -j ACCEPT # rate_limit = getValIfKeyExists( # xml_data["condition"]["traffic-flow-condition"], # "rate-limit") # The maximum number of connections per host. max_connections = getValIfKeyExists( xml_data["condition"]["traffic-flow-condition"], "max-connections") # If the action is DROP or REJECT the rule will fire an error # if max_src_conn is setted if max_connections is not None and default_target == "ACCEPT": # rule.max_src_nodes = max_connections rule.max_src_conn = max_connections rule.rule_flag = pf.PFRULE_SRCTRACK # Connections rate limit connections_rate = getValIfKeyExists( xml_data["condition"]["traffic-flow-condition"], "connections-rate") # If the action is DROP or REJECT the rule will fire an error # if max_src_conn_rate is setted if connections_rate is not None and default_target == "ACCEPT": splitted = connections_rate.split('/') # rule.max_src_nodes = max_connections limit = splitted[0] seconds = splitted[1] rule.max_src_conn_rate = (limit, seconds) rule.rule_flag = pf.PFRULE_SRCTRACK # In case of specified ports, ports can be applied only # if protocol is specified: if xml_sport is not None: if xml_proto is not None: if xml_proto.lower() == "tcp": Ports = pf.PFPort(xml_sport, socket.IPPROTO_TCP) elif xml_proto.lower() == "udp": Ports = pf.PFPort(xml_sport, socket.IPPROTO_UDP) else: Ports = pf.PFPort(xml_sport) if xml_dport is not None: if xml_proto is not None: if xml_proto.lower() == "tcp": Portd = pf.PFPort(xml_dport, socket.IPPROTO_TCP) elif xml_proto.lower() == "udp": Portd = pf.PFPort(xml_dport, socket.IPPROTO_UDP) else: print xml_dport Portd = pf.PFPort(xml_dport) # TODO: Manage Priority priority = getValIfKeyExists(xml_data, "priority") if Addrs is not None and Ports is not None: rule.src = pf.PFRuleAddr(Addrs, Ports) elif Addrs is not None: rule.src = pf.PFRuleAddr(Addrs) elif Ports is not None: rule.src = pf.PFRuleAddr(pf.PFAddr(), Ports) if Addrd is not None and Portd is not None: rule.dst = pf.PFRuleAddr(Addrd, Portd) elif Addrd is not None: rule.dst = pf.PFRuleAddr(Addrd) elif Portd is not None: rule.dst = pf.PFRuleAddr(pf.PFAddr(), Portd) # Append rule rules.append(rule) return rules