m3047 / shodohflo

Pure Python netflow and DNS correlation, with reusable Frame Streams, DnsTap and Protobuf implementations
Apache License 2.0
13 stars 0 forks source link

PCAP Agent: Rethinking Port Attribution #12

Open m3047 opened 2 weeks ago

m3047 commented 2 weeks ago

The PCAP Agent write several Redis keys:

When originally conceived, this agent was envisioned to run on the desktop / laptop at one end of most (if not all) observed conversations; and the desktop / laptop was not anticipated to provide any services. This understanding has evolved. First iteration was to run the agent on a span port on the switch. Presently the agent runs on the nodes offering virtualization, services, and / or routing.

In this last configuration port attribution is mostly incorrect.

Part of this is due to a technical limitation: the agent only captures traffic coming in to an interface, not outgoing traffic. But this is not a satisfying explanation.

Naively, a network either consumes services or offers services. The agent was assumed to be deployed on networks which consumed services. As such for outgoing traffic the resource is represented by the destination port, and for incoming traffic by the source port; traffic between two nodes on the own network is / was out of scope. None of this is satisfyingly true in the current evolution.

The flow key is documented like this:

<client-address>;<remote-address>;<remote-port>;flow

The nomenclature is representative of the original understanding. What current and future evolutions demand is something more like:

<client-address>;<server-address>;<service-port>;flow

There really is no "one size fits all" answer for this if networks can offer as well as consume resources. There is an observation which seems to be correct most of the time: the service's port will be lower than the client's port. This in turn suggests / is suggested by two tactical expediencies:

There are four network-centric flow cases to consider:

(The last case will probably never be seen on a properly managed network.)

Proposal

The proposal is to create a structured and extensible definition language which can be used in the configuration module to describe how each of the flow cases should be handled. This will be a DSL based on extensible Python objects, expressed using Python syntax.

An early whiteboard sketch of the concept looks something like this:

PORT_MAPPINGS = {
    'ours': {
        'ours': {
            'client': 'src',
            'server': 'dst',
            'port': 'dport'
        },
        'other':{
            ...
        }
    },
    'other': {
        ...
    }
}

But we need the ability to express or incorporate logic to e.g. use the lower of the two ports to decide which is the client and which is the server, especially for traffic which is between two nodes on our own network.

So this is still at least somewhat under investigation although I am closer to writing code than the foregoing may suggest.

I invite your feedback and suggestions.

m3047 commented 1 week ago

I've pushed my proposed enhancement to fwm so I can do smoketest. There may be changes.

When that's done I'll post again saying that it's gone to soak. It will stay in soak for at least a week or two before I merge it to master... there may be additional doc, field notes, etc. by that point.

m3047 commented 5 days ago

We are now in soak and the field is open for practice on the fwm branch.

It's particular to my network's architecture, but my networks / mapping looks like this:

NETWORK_ENUMERATION = NetworkEnumeration(
            (   'our_nets',      OUR_4NETS          ),
            (   'bert',         '10.33.0.1/32'      ),
            (   'ernie',        '10.33.0.2/32'      ),
            (   'external',     '0.0.0.0/0'         )
        )
FLOW_MAPPING = FlowMapping(
            (    None,      'our_nets',     LowerPort( SRC )                            ),
            (   'our_nets',  None,          LowerPort( DST )                            ),
            (   'external', 'bert',         Assign( DST, SRC, { 53 }, drop=True )       ),
            (   'external', 'bert',         Assign( SRC, DST, { 80, 443 }, drop=True )  ),
            (    None,      'bert',         LowerPort()                                 ),
            (    None,      'ernie',        LowerPort()                                 )
        )

The 10.*.*.*/32 addresses are actually routable addresses in real life.