# ============================================================ # lab-net-firewall — segmentacion y default-deny de salida # ------------------------------------------------------------ # Lo usa la leccion f02-firewall. Es el UNICO laboratorio de la # fase con una red que tiene salida a proposito: la leccion trata # justo de cortar esa salida y de comprobar que se corto. # # host # └── 127.0.0.1:8094 ──► entrada # │ # fw_dmz (CON salida, deliberado) # ┌────────────┬───────┴────────┬──────────────┐ # ▼ ▼ ▼ ▼ # entrada atacante recolector api # │ # fw_interna (internal: true) # ┌───────────┴──┐ # ▼ ▼ # datos api # # Dos controles distintos, y conviene no confundirlos: # # 1. SEGMENTACION, en el compose. "datos" solo esta en # fw_interna, asi que el atacante no tiene ni ruta hacia el. # Esto es L3 y lo hace Docker por ti. # # 2. EGRESS, dentro de "api", con iptables y politica DROP en # OUTPUT. Esto es lo que impide que un proceso comprometido # llame a casa. Un filtro por interfaz no basta: la salida # se controla en la pila del propio workload. # # docker compose up -d --build levantar # docker compose down -v destruir # ============================================================ name: lab-net-firewall networks: # Publica el puerto en el host. Nada mas. fw_borde: driver: bridge # ATENCION: esta red SI tiene salida a internet, y es la unica # de toda la fase que la tiene. Sin ella no se puede demostrar # que el control de egress hace algo. Todo objetivo real del # ejercicio esta dentro de este compose. fw_dmz: driver: bridge # Segmento de datos: sin salida y sin el atacante dentro. fw_interna: driver: bridge internal: true volumes: datos_pg: services: # --- borde ------------------------------------------------ entrada: image: nginx:1.27-alpine container_name: labnetfw-entrada ports: - "127.0.0.1:8094:80" volumes: - ./conf/entrada.conf:/etc/nginx/conf.d/default.conf:ro networks: - fw_borde - fw_dmz depends_on: - api restart: unless-stopped # --- la aplicacion, con pie en dos segmentos -------------- # NET_ADMIN es lo que permite escribir reglas en su propia # tabla. Se lo damos a proposito: el ejercicio consiste en que # tu apliques el default-deny y compruebes el antes y el # despues. En produccion esa politica la aplica la plataforma, # no el contenedor. api: build: ./api image: labnetfw-api container_name: labnetfw-api cap_add: - NET_ADMIN volumes: - ./reglas:/opt/reglas:ro - ./sitio:/usr/share/nginx/html:ro - ./conf/api.conf:/etc/nginx/conf.d/default.conf:ro networks: - fw_dmz - fw_interna restart: unless-stopped # --- segmento de datos ------------------------------------ # Sin puertos publicados y solo en fw_interna. El atacante no # tiene ruta hasta aqui, y eso no depende de ninguna regla. datos: image: postgres:16-alpine container_name: labnetfw-datos environment: POSTGRES_PASSWORD: laboratorio_local POSTGRES_USER: lab POSTGRES_DB: lab volumes: - datos_pg:/var/lib/postgresql/data networks: - fw_interna restart: unless-stopped # --- puesto del atacante ---------------------------------- atacante: build: ./herramientas image: labnetfw-herramientas container_name: labnetfw-atacante command: ["sleep", "infinity"] networks: - fw_dmz restart: unless-stopped # --- destino de exfiltracion, dentro del laboratorio ------ # Escucha en el 9000 y escribe en su log lo que reciba. Es el # "servidor del atacante" y vive aqui dentro justamente para # que el ejercicio no apunte a nada de fuera. recolector: build: ./herramientas image: labnetfw-herramientas container_name: labnetfw-recolector command: ["sh", "-c", "while true; do nc -l -p 9000 -v; done"] networks: - fw_dmz restart: unless-stopped