MitMproxy on Wifi with VLANs

MitMproxy on Wifi with VLANs

This is a three step guide to building a portable mitmproxy cascade with conatainers.

Step 1: The container

The following compose file creates the whole container:

version: '3.9'
services:
  mitmproxy:
    image: mitmproxy/mitmproxy
    command: >
      sh -c "apt-get update && apt-get -y install iptables iproute2 &&
             iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 &&
             iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8080 &&
             iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE &&
             mitmweb --web-host 0.0.0.0"
    ports:
      - '127.0.0.1:8083:8081'
    tty: true
    stdin_open: true
    networks:
      incoming:
      gate:
    cap_add:
      - NET_ADMIN
    sysctls:
      # don't know which one actually works, so I set both
      - net.ipv4.conf.all.forwarding=1
      - net.ipv4.ip_forward=1

networks:
  incoming:
    driver: macvlan
    driver_opts:
      parent: enp34s0.10
      #ipvlan_mode: l2
      macvlan_mode: bridge
    ipam:
      config:
      - subnet: 192.168.91.0/24
        #gateway: 192.168.91.1
  gate:

The network incoming will be reachable from outside the docker host with the vlan id 10. By allowing ip forwarding in the sysctls: section the traffic will be routed through the default gateway on the gate network. The gate network will have the eth0 network name and therefore we need to place the MASQUERADE rules on there.

Step 2: Create a Wifi with a tagged vlan

There are pretty nice GUIs for this by now. In Unifi this should be an easy option. If you own a Cisco AP like I do it's also pretty easy:

You also need to setup dhcp on your vlan. I did that with my Cisco AP too, but you might also do this inside the container.

Step 3: Creating the nginx backend

You still have to reach the web interface of the mitmproxy container. The compose config only forwards from localhost to the container's web interface, which is barely useful. So we put a nginx front on that with a nice domain and everything:

server {
  listen :80 ;
  server_name   mitm.domain.tld;
  
  # Might add ssl
  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /srv/http/acme.sh/;
  }
  
  location / {
    # This might look weird, but the dns rebinding
    # protection demands this stupid workaround

    proxy_set_header Host localhost:8083;
    proxy_set_header Origin http://localhost:8083;
    proxy_pass http://127.0.0.1:8083;

    expires off;
    proxy_http_version 1.1;
    proxy_redirect http://$http_host:8083 http://$http_host;
    proxy_buffering off;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Cookie $http_cookie;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
  access_log off;
}

That should be it.