ANS Lab 07 - Catch a packet with Traffic Mirroring

Scaffold: 3/5. The VPC, the source instance, the capture instance, the internal Network Load Balancer with its UDP listener on 4789, and the traffic mirror target are all built. The traffic mirror filter is built too, and it is empty. What is missing is the two filter rules and the mirror session. You add them, redeploy, and a tcpdump on the capture instance goes from silence to VXLAN.

The scenario

Flow logs tell you a conversation happened. They do not tell you what was in it: no sequence numbers, no flags, no window sizes, no payload. When the question is “what did the packets actually look like”, Traffic Mirroring is the only AWS service that answers it, because it is the only one that copies packets.

Copying packets is not free of consequence. Mirrored traffic is encapsulated in VXLAN and counts against the source instance’s own bandwidth and packet-per-second allowances, so mirroring an interface carrying 1 Gbps in and 1 Gbps out asks that instance to handle 4 Gbps. When the allowance runs out, mirrored packets are dropped first so production traffic keeps moving. That is the design constraint the filter exists to manage: mirror the flow you are investigating, keep the first hundred bytes or so, and leave everything else on the wire.

This lab builds the whole path and leaves you the two pieces that decide what crosses it.

What’s provided

The source instance opens one TCP connection a second to each of three ports on the capture instance: 443, 8080 and 9999. Nothing listens on any of them, so every probe is a SYN out and a RST back, which gives you traffic in both directions on three ports with no software to install.

Deploying src/template.yaml as shipped succeeds. Everything comes up, and nothing is mirrored, because a filter with no rules mirrors nothing.

Your task

Open src/template.yaml and fill in the two TODOs at the bottom.

The rules. Two AWS::EC2::TrafficMirrorFilterRule resources on MirrorFilter, both accepting TCP (Protocol: 6) between 10.20.0.0/16 and 10.20.0.0/16, and both naming port 443 only:

  MirrorRuleEgress:
    Type: AWS::EC2::TrafficMirrorFilterRule
    Properties:
      TrafficMirrorFilterId: !Ref MirrorFilter
      TrafficDirection: egress
      RuleNumber: 100
      RuleAction: accept
      Protocol: 6
      SourceCidrBlock: 10.20.0.0/16
      DestinationCidrBlock: 10.20.0.0/16
      DestinationPortRange:
        FromPort: 443
        ToPort: 443

The ingress rule is the same shape with TrafficDirection: ingress and a SourcePortRange of 443 to 443, because the reply comes back from 443 to an ephemeral port. Rules are directional and rule numbers are unique per direction, so both can be 100.

The session. One AWS::EC2::TrafficMirrorSession joining the three pieces:

  MirrorSession:
    Type: AWS::EC2::TrafficMirrorSession
    Properties:
      NetworkInterfaceId: !Ref SourceEni
      TrafficMirrorTargetId: !Ref MirrorTarget
      TrafficMirrorFilterId: !Ref MirrorFilter
      SessionNumber: 1
      PacketLength: 96
      VirtualNetworkId: 4242

PacketLength is bytes of the original packet, after the VXLAN header. 96 keeps the Ethernet, IP and TCP headers, including sequence numbers, flags and window size, and leaves the payload behind.

Run it

# Defaults: stack ans-lab-07, region ap-southeast-2.
./scripts/deploy.sh          # deploys src/template.yaml
./scripts/test.sh            # tcpdumps the capture instance and reads the result
./scripts/teardown.sh        # deletes everything

test.sh checks for a session on the source interface, checks the filter has rules, waits for the capture instance to pass its health check, then runs tcpdump -i any -n 'udp port 4789' on it through Systems Manager and counts what came back.

What success looks like

./scripts/test.sh prints the first few decoded packets, the counts, and then:

PASS: mirrored packets arrived VXLAN-encapsulated on UDP 4789, port 443 is
in the capture in both directions, and the two ports the filter did not
name are absent. The filter chose what crossed; the session carried it.

Widen it

Once it passes, do the experiment the whole feature is built around. Delete the DestinationPortRange and SourcePortRange from the two rules, remove PacketLength from the session, redeploy and run test.sh again. It now fails, because 8080 and 9999 are in the capture, and that failure is the finding: the capture also contains every byte of payload, and the mirrored copies are being carried by the source instance’s own network allowance. At this instance size and this traffic rate nothing gets dropped. At production rates the arithmetic bites, and the packets it drops are the mirrored ones.

If it fails

Reveal the solution

Deploy the complete reference template without editing anything:

SRC=solution ./scripts/deploy.sh && ./scripts/test.sh

What you just learned

Next

The rest of the Advanced Networking lab track is listed alongside the written posts at The Exam Room.