lab-net-captura — sockets, trafico y resolucion de nombres ========================================================== ESTADO: LABORATORIO LOCAL Target: http://localhost:8091 Autorizacion: sistema local creado por ti para entrenamiento. Todo lo que captures aqui es trafico tuyo, entre contenedores tuyos, en tu maquina. Capturar trafico de una red que no es tuya es un delito en casi cualquier jurisdiccion, y este laboratorio existe justamente para que no te haga falta. Lo usan dos lecciones: f02-tcpip TCP/IP, puertos y que escucha de verdad f02-dns DNS, DHCP y ARP: resolucion y suplantacion Que levanta ----------- entrada nginx, unico puerto publicado, solo en 127.0.0.1:8091 app aplicacion objetivo, sin puerto publicado db postgres, sin puerto publicado (segundo protocolo) cliente alpine con curl, dig, tcpdump, ss, nc y socat sniffer el mismo alpine, pero DENTRO de la pila de red de app Dos redes: cap_borde tiene salida; solo la toca "entrada" cap_interna internal: true, SIN salida a internet Comandos -------- docker compose up -d --build levantar (la primera vez construye) docker compose ps ver que corre docker compose exec cliente sh entrar al puesto de trabajo docker compose logs -f entrada seguir los logs del borde docker compose down -v destruir todo Abre despues: http://localhost:8091 Por que el sniffer comparte la red de app ----------------------------------------- Un bridge de Linux aprende direcciones MAC. Una vez aprendidas, el trafico entre dos contenedores NO se reenvia a los demas puertos del bridge, asi que un tcpdump "al lado" solo veria difusion y poco mas. Por eso el sniffer usa network_mode: "service:app": comparte el espacio de nombres de red del contenedor app y ve exactamente lo que ve app, incluido su loopback. La consecuencia practica que importa en seguridad es que para capturar hace falta estar dentro de la pila de red del objetivo, es decir, hace falta ejecucion. Recorrido de la leccion f02-tcpip --------------------------------- # 1. que escucha DENTRO de cada contenedor docker compose exec cliente ss -ltnp docker compose exec sniffer ss -ltnp # ve los sockets de app # 2. que expone el host de verdad docker compose ps ss -ltn | grep 8091 # en tu maquina, no en el contenedor # 3. la diferencia entre "escucha" y "es alcanzable" docker compose exec cliente curl -s -o /dev/null -w '%{http_code}\n' http://app/ curl -m 3 -s -o /dev/null -w '%{http_code}\n' http://localhost:8091/ # 4. un puerto abierto, uno cerrado y uno filtrado docker compose exec cliente nc -z -w 2 app 80 ; echo salida=$? docker compose exec cliente nc -z -w 2 app 3306 ; echo salida=$? Recorrido de la leccion f02-dns ------------------------------- # el resolutor que usa el contenedor (direccion loopback de Docker) docker compose exec cliente cat /etc/resolv.conf # resolucion de un nombre del propio laboratorio docker compose exec cliente dig +short app docker compose exec cliente dig app # captura de la consulta, desde dentro de la pila de red de app docker compose exec -d sniffer sh -c "tcpdump -i any -w /tmp/dns.pcap port 53" docker compose exec cliente curl -s http://app/ > /dev/null docker compose exec sniffer sh -c "pkill tcpdump; tcpdump -nr /tmp/dns.pcap | head" # captura de una sesion TCP completa (handshake incluido) docker compose exec -d sniffer sh -c "tcpdump -i any -w /tmp/http.pcap tcp port 80" docker compose exec cliente curl -s http://app/salud docker compose exec sniffer sh -c "pkill tcpdump; tcpdump -r /tmp/http.pcap | head -20" Si algo falla ------------- "port is already allocated" otro proceso usa el 8091; cambia el mapeo a 127.0.0.1:8092:80 tcpdump: permission denied falta NET_RAW; comprueba el cap_add del servicio sniffer el cliente llega a internet revisa que cap_interna siga con internal: true y que el contenedor no este tambien en cap_borde la captura sale vacia estas capturando en el contenedor equivocado: el sniffer ve la red de app, no la del cliente Cuando termines, siempre: docker compose down -v