예제 #1
0
파일: app.py 프로젝트: nahun/pfweb
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
예제 #2
0
파일: app.py 프로젝트: nahun/pfweb
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
예제 #3
0
    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))
예제 #4
0
파일: app.py 프로젝트: nahun/pfweb
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
예제 #5
0
파일: app.py 프로젝트: nahun/pfweb
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)
예제 #6
0
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