feat: scaffold inicial de web_proxy (proxy de interceptacion liviano)
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
# Estado local y capturas — no se versionan
|
||||||
|
local_files/
|
||||||
|
captures/
|
||||||
|
*.mitm
|
||||||
|
*.har
|
||||||
|
*.pid
|
||||||
|
*.log
|
||||||
|
chromium-profile/
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
name: web_proxy
|
||||||
|
lang: bash
|
||||||
|
domain: cybersecurity
|
||||||
|
version: 0.1.0
|
||||||
|
description: "Proxy de interceptacion HTTP/HTTPS liviano y siempre activo (mitmproxy) para capturar todo el trafico de un navegador con rotacion temporal de capturas, alternativa ligera a ZAP/Burp."
|
||||||
|
tags: [service, web-proxy, mitmproxy, proxy, capture, cybersecurity, chromium]
|
||||||
|
icon:
|
||||||
|
phosphor: "shield-check"
|
||||||
|
accent: "#7c3aed"
|
||||||
|
uses_functions:
|
||||||
|
- start_mitm_capture_bash_cybersecurity
|
||||||
|
- query_mitm_flows_bash_cybersecurity
|
||||||
|
- rotate_capture_flows_py_cybersecurity
|
||||||
|
- launch_chromium_proxy_bash_browser
|
||||||
|
- port_kill_bash_infra
|
||||||
|
uses_types: []
|
||||||
|
framework: ""
|
||||||
|
entry_point: "web_proxy"
|
||||||
|
dir_path: "apps/web_proxy"
|
||||||
|
repo_url: ""
|
||||||
|
service:
|
||||||
|
port: 8080
|
||||||
|
health_endpoint: null
|
||||||
|
health_timeout_s: 3
|
||||||
|
systemd_unit: web_proxy.service
|
||||||
|
systemd_scope: user
|
||||||
|
restart_policy: always
|
||||||
|
runtime: systemd-user
|
||||||
|
pc_targets:
|
||||||
|
- home-wsl
|
||||||
|
is_local_only: true
|
||||||
|
e2e_checks:
|
||||||
|
- id: syntax
|
||||||
|
cmd: "bash -n web_proxy"
|
||||||
|
timeout_s: 10
|
||||||
|
- id: help
|
||||||
|
cmd: "./web_proxy help"
|
||||||
|
expect_stdout_contains: "proxy de interceptacion"
|
||||||
|
timeout_s: 10
|
||||||
|
- id: capture_roundtrip
|
||||||
|
cmd: "bash tests/e2e_capture.sh"
|
||||||
|
expect_stdout_contains: "CAPTURE_OK"
|
||||||
|
timeout_s: 40
|
||||||
|
---
|
||||||
|
|
||||||
|
# web_proxy
|
||||||
|
|
||||||
|
Proxy de interceptacion HTTP/HTTPS liviano y siempre activo, pensado como
|
||||||
|
alternativa ligera a OWASP ZAP y Burp Suite. Construido sobre `mitmproxy`
|
||||||
|
(footprint de decenas de MB frente a la JVM de ZAP/Burp) y compuesto a partir
|
||||||
|
de funciones del registry `fn_registry`.
|
||||||
|
|
||||||
|
## Que hace
|
||||||
|
|
||||||
|
- Levanta un proxy local (`mitmdump` headless) que registra **todo** el trafico
|
||||||
|
HTTP/HTTPS que pasa por el.
|
||||||
|
- **Rota las capturas** en archivos `.mitm` por ventanas de tiempo configurables
|
||||||
|
(default 20 minutos), de modo que puedas consultar el trafico de cada tramo
|
||||||
|
por separado.
|
||||||
|
- Corre **siempre activo** como servicio `systemd --user` con `Restart=always`,
|
||||||
|
o de forma manual con `start`/`stop`.
|
||||||
|
- Lanza un **Chromium proxeado** en un perfil aislado para no contaminar tu
|
||||||
|
navegacion normal.
|
||||||
|
- Permite **consultar** las capturas guardadas: volcado filtrado, exportacion a
|
||||||
|
HAR, o inspeccion visual en `mitmweb`.
|
||||||
|
|
||||||
|
## Ejemplo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/web_proxy
|
||||||
|
|
||||||
|
# Servicio siempre activo en el puerto 8080, capturas en ~/captures, rotando cada 20 min
|
||||||
|
./web_proxy install-service --port 8080 --out ~/captures --rotate-min 20
|
||||||
|
|
||||||
|
# Confiar en el CA de mitmproxy para que HTTPS no rompa
|
||||||
|
./web_proxy ca
|
||||||
|
|
||||||
|
# Navegar con todo el trafico capturado (Chromium en perfil aislado)
|
||||||
|
./web_proxy browser --url https://example.com
|
||||||
|
|
||||||
|
# Consultar la ultima ventana de captura
|
||||||
|
./web_proxy query "~m POST" # solo peticiones POST
|
||||||
|
./web_proxy query "~c 500" --all # respuestas 5xx en todas las capturas
|
||||||
|
./web_proxy har ~/captures/sesion.har
|
||||||
|
|
||||||
|
# Inspeccion visual en el navegador
|
||||||
|
./web_proxy inspect
|
||||||
|
|
||||||
|
# Estado y logs
|
||||||
|
./web_proxy status
|
||||||
|
./web_proxy logs
|
||||||
|
```
|
||||||
|
|
||||||
|
Modo manual (sin servicio):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./web_proxy start --port 8080 --out ~/captures --rotate-min 20
|
||||||
|
./web_proxy browser
|
||||||
|
./web_proxy query "~d api.github.com"
|
||||||
|
./web_proxy stop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cuando usarla
|
||||||
|
|
||||||
|
Cuando quieras registrar de forma continua y liviana todo lo que hace un
|
||||||
|
navegador (debug de APIs, auditoria de trafico, inspeccion de requests de una
|
||||||
|
SPA) sin arrancar la maquinaria pesada de ZAP o Burp, y poder volver a consultar
|
||||||
|
sesiones pasadas troceadas por ventanas de tiempo.
|
||||||
|
|
||||||
|
## Como consultar las capturas
|
||||||
|
|
||||||
|
Las capturas son archivos `.mitm` en el directorio de salida (`~/captures` por
|
||||||
|
defecto), con nombre `traffic-YYYYmmdd-HHMMSS.mitm`, uno por ventana de rotacion.
|
||||||
|
|
||||||
|
| Forma | Comando |
|
||||||
|
|---|---|
|
||||||
|
| Volcado filtrado (CLI) | `./web_proxy query "~m POST"` |
|
||||||
|
| Volcado de todas las ventanas | `./web_proxy query "~u /api" --all` |
|
||||||
|
| Exportar a HAR | `./web_proxy har salida.har` |
|
||||||
|
| Inspeccion visual (UI web) | `./web_proxy inspect [archivo.mitm]` |
|
||||||
|
| Directo con mitmproxy | `mitmproxy -r ~/captures/traffic-*.mitm` |
|
||||||
|
|
||||||
|
Sintaxis de filtros de mitmproxy: `~u` URL, `~d` dominio, `~m` metodo, `~c`
|
||||||
|
codigo de estado, `~bq`/`~bs` cuerpo de peticion/respuesta. Combinables con
|
||||||
|
`&` (y), `|` (o), `!` (no).
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- **HTTPS**: requiere confiar en el CA de mitmproxy. Ejecuta `./web_proxy ca`
|
||||||
|
para las instrucciones. El navegador que lanza `./web_proxy browser` usa
|
||||||
|
`--ignore-certificate-errors` si no instalas el CA, lo cual es comodo pero
|
||||||
|
menos seguro (vale para uso local de captura, no para navegacion sensible).
|
||||||
|
- **Durabilidad**: el addon de rotacion hace `flush()` tras cada flujo, asi que
|
||||||
|
las capturas sobreviven a un `kill -9` o un corte abrupto del proceso.
|
||||||
|
- **Rotacion por trafico**: la rotacion ocurre al recibir un flujo; si no hay
|
||||||
|
trafico durante la ventana, el archivo no rota hasta el siguiente request.
|
||||||
|
- **Persistencia tras logout**: el servicio `systemd --user` para al cerrar
|
||||||
|
sesion salvo que actives linger con `./web_proxy install-service --linger`.
|
||||||
|
- **Conflicto de puerto**: `start` y el servicio no pueden coexistir en el mismo
|
||||||
|
puerto. `install-service` para el proxy manual automaticamente antes de
|
||||||
|
arrancar el servicio.
|
||||||
|
- **Stop fiable**: el `stop` mata por PID guardado en el pidfile (no depende de
|
||||||
|
`lsof`/`port_kill`, que en este entorno no siempre detecta el listener a
|
||||||
|
tiempo).
|
||||||
|
|
||||||
|
## Arquitectura
|
||||||
|
|
||||||
|
Orquesta funciones del registry (no reimplementa logica reutilizable):
|
||||||
|
|
||||||
|
| Funcion del registry | Rol |
|
||||||
|
|---|---|
|
||||||
|
| `start_mitm_capture_bash_cybersecurity` | Arranca `mitmdump` headless con rotacion |
|
||||||
|
| `rotate_capture_flows_py_cybersecurity` | Addon de mitmproxy que trocea las capturas |
|
||||||
|
| `query_mitm_flows_bash_cybersecurity` | Consulta capturas guardadas (volcado / HAR) |
|
||||||
|
| `launch_chromium_proxy_bash_browser` | Lanza Chromium proxeado en perfil aislado |
|
||||||
|
| `port_kill_bash_infra` | Limpieza de puertos ocupados |
|
||||||
|
|
||||||
|
El CLI `web_proxy` aporta solo la logica especifica de la app: gestion del
|
||||||
|
pidfile, configuracion persistente, generacion del unit `systemd --user` y la
|
||||||
|
interfaz de subcomandos.
|
||||||
Executable
+78
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# e2e_capture.sh — prueba de roundtrip completa del stack de captura.
|
||||||
|
#
|
||||||
|
# Levanta un servidor HTTP local, arranca el proxy mitmproxy a traves de las
|
||||||
|
# funciones del registry, hace pasar trafico por el, y verifica que:
|
||||||
|
# - se crea un archivo .mitm con contenido,
|
||||||
|
# - la captura sobrevive a un SIGKILL (flush por flujo),
|
||||||
|
# - la consulta a HAR devuelve las entradas esperadas con sus codigos.
|
||||||
|
#
|
||||||
|
# Emite "CAPTURE_OK" en stdout si todo pasa; cualquier otro caso es fallo.
|
||||||
|
# Diseñado para ser idempotente y autolimpiante (puertos altos, dir temporal).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$SCRIPT_DIR/../../.." && pwd)}"
|
||||||
|
export FN_REGISTRY_ROOT="$REGISTRY_ROOT"
|
||||||
|
|
||||||
|
START_FN="$REGISTRY_ROOT/bash/functions/cybersecurity/start_mitm_capture.sh"
|
||||||
|
QUERY_FN="$REGISTRY_ROOT/bash/functions/cybersecurity/query_mitm_flows.sh"
|
||||||
|
|
||||||
|
PROXY_PORT=18906
|
||||||
|
UPSTREAM_PORT=18913
|
||||||
|
WORK="$(mktemp -d /tmp/web_proxy_e2e.XXXXXX)"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
[[ -n "${PROXY_PID:-}" ]] && kill -9 "$PROXY_PID" 2>/dev/null
|
||||||
|
[[ -n "${SRV_PID:-}" ]] && kill -9 "$SRV_PID" 2>/dev/null
|
||||||
|
rm -rf "$WORK"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
fail() { echo "CAPTURE_FAIL: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
mkdir -p "$WORK/www" "$WORK/cap"
|
||||||
|
echo "<html><body>web_proxy e2e</body></html>" > "$WORK/www/index.html"
|
||||||
|
|
||||||
|
# --- 1. Upstream HTTP local (respuesta valida garantizada) -------------------
|
||||||
|
setsid bash -c "cd '$WORK/www' && exec python3 -m http.server $UPSTREAM_PORT" </dev/null >/dev/null 2>&1 &
|
||||||
|
SRV_PID=$!
|
||||||
|
disown "$SRV_PID" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# --- 2. Arrancar el proxy via funcion del registry ---------------------------
|
||||||
|
JSON="$(bash "$START_FN" --port "$PROXY_PORT" --out "$WORK/cap" --rotate-min 20)" \
|
||||||
|
|| fail "start_mitm_capture fallo"
|
||||||
|
PROXY_PID="$(printf '%s' "$JSON" | python3 -c 'import sys,json;print(json.load(sys.stdin)["pid"])' 2>/dev/null)"
|
||||||
|
[[ -n "$PROXY_PID" ]] || fail "no se obtuvo PID del proxy ($JSON)"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# --- 3. Trafico via proxy ----------------------------------------------------
|
||||||
|
for path in / /index.html /alpha /beta; do
|
||||||
|
curl -s -x "http://127.0.0.1:$PROXY_PORT" "http://127.0.0.1:$UPSTREAM_PORT${path}" -o /dev/null \
|
||||||
|
|| fail "curl fallo en $path"
|
||||||
|
done
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# --- 4. Verificar captura ----------------------------------------------------
|
||||||
|
MITM="$(find "$WORK/cap" -name 'traffic-*.mitm' | head -1)"
|
||||||
|
[[ -n "$MITM" ]] || fail "no se creo archivo .mitm"
|
||||||
|
BYTES_BEFORE="$(stat -c%s "$MITM")"
|
||||||
|
[[ "$BYTES_BEFORE" -gt 0 ]] || fail "el .mitm quedo vacio antes de matar"
|
||||||
|
|
||||||
|
# --- 5. Durabilidad: SIGKILL no debe perder datos ----------------------------
|
||||||
|
kill -9 "$PROXY_PID" 2>/dev/null
|
||||||
|
PROXY_PID=""
|
||||||
|
sleep 1
|
||||||
|
BYTES_AFTER="$(stat -c%s "$MITM")"
|
||||||
|
[[ "$BYTES_AFTER" -eq "$BYTES_BEFORE" && "$BYTES_AFTER" -gt 0 ]] \
|
||||||
|
|| fail "la captura no sobrevivio al SIGKILL ($BYTES_BEFORE -> $BYTES_AFTER)"
|
||||||
|
|
||||||
|
# --- 6. Consulta a HAR -------------------------------------------------------
|
||||||
|
bash "$QUERY_FN" "$MITM" --har "$WORK/out.har" >/dev/null 2>&1 || fail "export HAR fallo"
|
||||||
|
ENTRIES="$(python3 -c "import json;print(len(json.load(open('$WORK/out.har'))['log']['entries']))" 2>/dev/null)"
|
||||||
|
[[ "$ENTRIES" == "4" ]] || fail "HAR esperaba 4 entradas, obtuvo ${ENTRIES:-0}"
|
||||||
|
|
||||||
|
echo "CAPTURE_OK ($ENTRIES flujos, ${BYTES_AFTER}B, sobrevivio SIGKILL)"
|
||||||
@@ -0,0 +1,464 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# web_proxy — proxy de interceptacion HTTP/HTTPS liviano, siempre activo.
|
||||||
|
#
|
||||||
|
# Orquesta funciones del registry fn_registry para capturar todo el trafico de
|
||||||
|
# un navegador (estilo ZAP/Burp) con un footprint minimo basado en mitmproxy:
|
||||||
|
#
|
||||||
|
# start_mitm_capture_bash_cybersecurity arranca mitmdump con rotacion
|
||||||
|
# rotate_capture_flows_py_cybersecurity addon que trocea las capturas
|
||||||
|
# query_mitm_flows_bash_cybersecurity consulta capturas guardadas
|
||||||
|
# launch_chromium_proxy_bash_browser navegador proxeado en perfil aislado
|
||||||
|
#
|
||||||
|
# El proxy puede correr en dos modos:
|
||||||
|
# - manual: `web_proxy start` / `web_proxy stop` (background + pidfile)
|
||||||
|
# - servicio: `web_proxy install-service` (systemd --user, Restart=always,
|
||||||
|
# siempre activo, sobrevive a logout/reboot con linger)
|
||||||
|
#
|
||||||
|
# Estado y configuracion viven en $WEB_PROXY_HOME (default ~/.web_proxy).
|
||||||
|
# Las capturas .mitm viven en el directorio de salida (default ~/captures).
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# --- Resolucion de rutas -----------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
# apps/web_proxy/ -> raiz del registry son dos niveles arriba.
|
||||||
|
REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
||||||
|
export FN_REGISTRY_ROOT="$REGISTRY_ROOT"
|
||||||
|
|
||||||
|
FN_BIN="$REGISTRY_ROOT/fn"
|
||||||
|
ADDON_PATH="$REGISTRY_ROOT/python/functions/cybersecurity/rotate_capture_flows.py"
|
||||||
|
|
||||||
|
START_FN="$REGISTRY_ROOT/bash/functions/cybersecurity/start_mitm_capture.sh"
|
||||||
|
QUERY_FN="$REGISTRY_ROOT/bash/functions/cybersecurity/query_mitm_flows.sh"
|
||||||
|
BROWSER_FN="$REGISTRY_ROOT/bash/functions/browser/launch_chromium_proxy.sh"
|
||||||
|
|
||||||
|
# --- Configuracion -----------------------------------------------------------
|
||||||
|
|
||||||
|
WEB_PROXY_HOME="${WEB_PROXY_HOME:-$HOME/.web_proxy}"
|
||||||
|
PIDFILE="$WEB_PROXY_HOME/web_proxy.pid"
|
||||||
|
CONFFILE="$WEB_PROXY_HOME/web_proxy.conf"
|
||||||
|
SERVICE_NAME="web_proxy.service"
|
||||||
|
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
|
||||||
|
|
||||||
|
DEFAULT_PORT=8080
|
||||||
|
DEFAULT_OUT="$HOME/captures"
|
||||||
|
DEFAULT_ROTATE=20
|
||||||
|
|
||||||
|
MITMDUMP_BIN="$(command -v mitmdump 2>/dev/null || echo "$HOME/.local/bin/mitmdump")"
|
||||||
|
MITMWEB_BIN="$(command -v mitmweb 2>/dev/null || echo "$HOME/.local/bin/mitmweb")"
|
||||||
|
CA_CERT="$HOME/.mitmproxy/mitmproxy-ca-cert.pem"
|
||||||
|
|
||||||
|
mkdir -p "$WEB_PROXY_HOME"
|
||||||
|
|
||||||
|
# --- Helpers -----------------------------------------------------------------
|
||||||
|
|
||||||
|
err() { printf '\033[31m%s\033[0m\n' "$*" >&2; }
|
||||||
|
ok() { printf '\033[32m%s\033[0m\n' "$*"; }
|
||||||
|
info() { printf '%s\n' "$*"; }
|
||||||
|
|
||||||
|
# Lee KEY del conffile (formato KEY=VAL), o imprime el default pasado.
|
||||||
|
conf_get() {
|
||||||
|
local key="$1" def="${2:-}"
|
||||||
|
if [[ -f "$CONFFILE" ]]; then
|
||||||
|
local val
|
||||||
|
val="$(grep -E "^${key}=" "$CONFFILE" 2>/dev/null | tail -1 | cut -d= -f2-)"
|
||||||
|
[[ -n "$val" ]] && { printf '%s' "$val"; return; }
|
||||||
|
fi
|
||||||
|
printf '%s' "$def"
|
||||||
|
}
|
||||||
|
|
||||||
|
conf_write() {
|
||||||
|
local port="$1" out="$2" rotate="$3"
|
||||||
|
cat > "$CONFFILE" <<EOF
|
||||||
|
PORT=$port
|
||||||
|
OUT=$out
|
||||||
|
ROTATE=$rotate
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# PID del proxy manual, si vive. Imprime el PID o nada.
|
||||||
|
running_pid() {
|
||||||
|
[[ -f "$PIDFILE" ]] || return 1
|
||||||
|
local pid
|
||||||
|
pid="$(cat "$PIDFILE" 2>/dev/null)"
|
||||||
|
[[ -n "$pid" ]] || return 1
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
printf '%s' "$pid"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
service_active() {
|
||||||
|
systemctl --user is-active --quiet "$SERVICE_NAME" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Subcomandos -------------------------------------------------------------
|
||||||
|
|
||||||
|
cmd_start() {
|
||||||
|
local port out rotate
|
||||||
|
port="$DEFAULT_PORT"; out="$DEFAULT_OUT"; rotate="$DEFAULT_ROTATE"
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--port) port="$2"; shift 2 ;;
|
||||||
|
--out) out="$2"; shift 2 ;;
|
||||||
|
--rotate-min) rotate="$2"; shift 2 ;;
|
||||||
|
*) err "start: flag desconocido: $1"; return 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if service_active; then
|
||||||
|
err "El servicio systemd ($SERVICE_NAME) ya esta activo. Usa 'web_proxy stop-service' o gestiona el servicio en su lugar."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if running_pid >/dev/null; then
|
||||||
|
err "Ya hay un proxy manual corriendo (PID $(running_pid)). Para con 'web_proxy stop'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local json
|
||||||
|
json="$(bash "$START_FN" --port "$port" --out "$out" --rotate-min "$rotate")" || {
|
||||||
|
err "Fallo al arrancar mitmdump."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
local pid
|
||||||
|
pid="$(printf '%s' "$json" | python3 -c 'import sys,json;print(json.load(sys.stdin)["pid"])' 2>/dev/null)"
|
||||||
|
if [[ -z "$pid" ]]; then
|
||||||
|
err "No se pudo extraer el PID del arranque. Salida: $json"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
printf '%s' "$pid" > "$PIDFILE"
|
||||||
|
conf_write "$port" "$out" "$rotate"
|
||||||
|
ok "Proxy activo en 127.0.0.1:$port (PID $pid)"
|
||||||
|
info " capturas -> $out (rotacion cada ${rotate} min)"
|
||||||
|
info " navegador -> web_proxy browser"
|
||||||
|
info " consultar -> web_proxy query \"~m POST\""
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_stop() {
|
||||||
|
local pid
|
||||||
|
if pid="$(running_pid)"; then
|
||||||
|
kill -TERM "$pid" 2>/dev/null
|
||||||
|
# Esperar cierre limpio; SIGKILL si se resiste.
|
||||||
|
for _ in 1 2 3 4 5; do
|
||||||
|
kill -0 "$pid" 2>/dev/null || break
|
||||||
|
sleep 0.3
|
||||||
|
done
|
||||||
|
kill -9 "$pid" 2>/dev/null
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
ok "Proxy manual detenido (PID $pid)."
|
||||||
|
else
|
||||||
|
rm -f "$PIDFILE"
|
||||||
|
info "No habia proxy manual corriendo."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_restart() {
|
||||||
|
cmd_stop
|
||||||
|
sleep 1
|
||||||
|
cmd_start "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_status() {
|
||||||
|
local port out rotate
|
||||||
|
port="$(conf_get PORT "$DEFAULT_PORT")"
|
||||||
|
out="$(conf_get OUT "$DEFAULT_OUT")"
|
||||||
|
rotate="$(conf_get ROTATE "$DEFAULT_ROTATE")"
|
||||||
|
|
||||||
|
info "web_proxy — estado"
|
||||||
|
info " home: $WEB_PROXY_HOME"
|
||||||
|
info " capturas: $out"
|
||||||
|
info " rotacion: cada ${rotate} min"
|
||||||
|
|
||||||
|
local pid
|
||||||
|
if pid="$(running_pid)"; then
|
||||||
|
ok " manual: ACTIVO (PID $pid, puerto $port)"
|
||||||
|
else
|
||||||
|
info " manual: parado"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if service_active; then
|
||||||
|
ok " servicio: ACTIVO ($SERVICE_NAME)"
|
||||||
|
elif systemctl --user list-unit-files "$SERVICE_NAME" &>/dev/null && \
|
||||||
|
systemctl --user cat "$SERVICE_NAME" &>/dev/null; then
|
||||||
|
info " servicio: instalado, parado"
|
||||||
|
else
|
||||||
|
info " servicio: no instalado"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d "$out" ]]; then
|
||||||
|
local n size
|
||||||
|
n="$(find "$out" -maxdepth 1 -name 'traffic-*.mitm' 2>/dev/null | wc -l)"
|
||||||
|
size="$(du -sh "$out" 2>/dev/null | cut -f1)"
|
||||||
|
info " archivos: ${n} capturas (${size:-0})"
|
||||||
|
local last
|
||||||
|
last="$(find "$out" -maxdepth 1 -name 'traffic-*.mitm' -printf '%T@ %p\n' 2>/dev/null | sort -nr | head -1 | cut -d' ' -f2-)"
|
||||||
|
[[ -n "$last" ]] && info " ultima: $(basename "$last")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$CA_CERT" ]]; then
|
||||||
|
info " CA HTTPS: $CA_CERT"
|
||||||
|
else
|
||||||
|
info " CA HTTPS: no generado todavia (arranca el proxy una vez)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_browser() {
|
||||||
|
local url="" proxy_port
|
||||||
|
proxy_port="$(conf_get PORT "$DEFAULT_PORT")"
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--url) url="$2"; shift 2 ;;
|
||||||
|
--port) proxy_port="$2"; shift 2 ;;
|
||||||
|
*) err "browser: flag desconocido: $1"; return 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! running_pid >/dev/null && ! service_active; then
|
||||||
|
err "No hay proxy activo. Arranca primero con 'web_proxy start' o instala el servicio."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local args=(--proxy "http://127.0.0.1:${proxy_port}" --profile "$WEB_PROXY_HOME/chromium-profile")
|
||||||
|
# Si el CA esta instalado en el perfil, no forzamos --ignore-certificate-errors.
|
||||||
|
[[ -n "$url" ]] && args+=(--url "$url")
|
||||||
|
bash "$BROWSER_FN" "${args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resuelve la lista de archivos a consultar: por defecto la ultima captura.
|
||||||
|
resolve_capture_files() {
|
||||||
|
local out scope="$1"
|
||||||
|
out="$(conf_get OUT "$DEFAULT_OUT")"
|
||||||
|
if [[ "$scope" == "all" ]]; then
|
||||||
|
find "$out" -maxdepth 1 -name 'traffic-*.mitm' 2>/dev/null | sort
|
||||||
|
else
|
||||||
|
find "$out" -maxdepth 1 -name 'traffic-*.mitm' -printf '%T@ %p\n' 2>/dev/null \
|
||||||
|
| sort -nr | head -1 | cut -d' ' -f2-
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_query() {
|
||||||
|
local filter="" scope="last"
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--all) scope="all"; shift ;;
|
||||||
|
--last) scope="last"; shift ;;
|
||||||
|
*) filter="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
local files
|
||||||
|
mapfile -t files < <(resolve_capture_files "$scope")
|
||||||
|
if [[ ${#files[@]} -eq 0 || -z "${files[0]}" ]]; then
|
||||||
|
err "No hay capturas en $(conf_get OUT "$DEFAULT_OUT")."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ -n "$filter" ]]; then
|
||||||
|
bash "$QUERY_FN" "${files[@]}" --filter "$filter"
|
||||||
|
else
|
||||||
|
bash "$QUERY_FN" "${files[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_har() {
|
||||||
|
local outhar="${1:-$HOME/captures/export.har}" scope="last"
|
||||||
|
[[ "${2:-}" == "--all" ]] && scope="all"
|
||||||
|
local files
|
||||||
|
mapfile -t files < <(resolve_capture_files "$scope")
|
||||||
|
if [[ ${#files[@]} -eq 0 || -z "${files[0]}" ]]; then
|
||||||
|
err "No hay capturas para exportar."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
bash "$QUERY_FN" "${files[@]}" --har "$outhar"
|
||||||
|
ok "HAR exportado a $outhar"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_inspect() {
|
||||||
|
local file="${1:-}"
|
||||||
|
if [[ -z "$file" ]]; then
|
||||||
|
file="$(resolve_capture_files last)"
|
||||||
|
fi
|
||||||
|
if [[ -z "$file" || ! -f "$file" ]]; then
|
||||||
|
err "No hay captura para inspeccionar. Pasa una ruta o arranca el proxy."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
info "Abriendo mitmweb sobre $(basename "$file") -> http://127.0.0.1:8081"
|
||||||
|
info "(Ctrl-C para cerrar la UI; no afecta al proxy activo)"
|
||||||
|
"$MITMWEB_BIN" -r "$file" --no-web-open-browser
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_ca() {
|
||||||
|
info "HTTPS via proxy de interceptacion requiere confiar en el CA de mitmproxy."
|
||||||
|
info ""
|
||||||
|
if [[ ! -f "$CA_CERT" ]]; then
|
||||||
|
err "El CA aun no existe ($CA_CERT)."
|
||||||
|
info "Arranca el proxy una vez ('web_proxy start') para que mitmproxy lo genere."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
info "CA generado en: $CA_CERT"
|
||||||
|
info ""
|
||||||
|
info "Opciones:"
|
||||||
|
info " 1. Confianza a nivel sistema (recomendado):"
|
||||||
|
info " sudo cp $CA_CERT /usr/local/share/ca-certificates/mitmproxy.crt"
|
||||||
|
info " sudo update-ca-certificates"
|
||||||
|
info " 2. Solo para el navegador proxeado: importa el .pem en"
|
||||||
|
info " chrome://settings/certificates (pestaña Autoridades)."
|
||||||
|
info " 3. Rapido y sucio (menos seguro): el navegador de 'web_proxy browser'"
|
||||||
|
info " ya usa --ignore-certificate-errors si no instalas el CA."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Genera e instala el unit systemd --user. El servicio corre mitmdump en
|
||||||
|
# foreground (systemd gestiona el proceso) con Restart=always.
|
||||||
|
cmd_install_service() {
|
||||||
|
local port out rotate enable_linger="no"
|
||||||
|
port="$(conf_get PORT "$DEFAULT_PORT")"
|
||||||
|
out="$(conf_get OUT "$DEFAULT_OUT")"
|
||||||
|
rotate="$(conf_get ROTATE "$DEFAULT_ROTATE")"
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--port) port="$2"; shift 2 ;;
|
||||||
|
--out) out="$2"; shift 2 ;;
|
||||||
|
--rotate-min) rotate="$2"; shift 2 ;;
|
||||||
|
--linger) enable_linger="yes"; shift ;;
|
||||||
|
*) err "install-service: flag desconocido: $1"; return 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -x "$MITMDUMP_BIN" ]]; then
|
||||||
|
err "mitmdump no encontrado. Instala con: uv tool install mitmproxy"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Si hay un proxy manual corriendo, pararlo para no chocar de puerto.
|
||||||
|
if running_pid >/dev/null; then
|
||||||
|
info "Parando el proxy manual antes de instalar el servicio..."
|
||||||
|
cmd_stop
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$SYSTEMD_USER_DIR" "$out"
|
||||||
|
conf_write "$port" "$out" "$rotate"
|
||||||
|
|
||||||
|
cat > "$SYSTEMD_USER_DIR/$SERVICE_NAME" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=web_proxy — proxy de interceptacion HTTP/HTTPS liviano (mitmproxy)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=$MITMDUMP_BIN -s $ADDON_PATH --set rotate_min=$rotate --set capture_dir=$out --listen-port $port
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
# Restart=always (no on-failure): un SIGTERM limpio es exit success y
|
||||||
|
# on-failure NO reiniciaria, dejando el proxy muerto en silencio.
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now "$SERVICE_NAME"
|
||||||
|
|
||||||
|
if [[ "$enable_linger" == "yes" ]]; then
|
||||||
|
loginctl enable-linger "$USER" 2>/dev/null \
|
||||||
|
&& ok "Linger activado: el servicio sigue vivo aunque cierres sesion." \
|
||||||
|
|| err "No se pudo activar linger (requiere permisos). El servicio para al cerrar sesion."
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if service_active; then
|
||||||
|
ok "Servicio instalado y ACTIVO en 127.0.0.1:$port."
|
||||||
|
info " capturas -> $out (rotacion cada ${rotate} min)"
|
||||||
|
info " logs -> web_proxy logs"
|
||||||
|
info " navegador -> web_proxy browser"
|
||||||
|
[[ "$enable_linger" == "no" ]] && info " persistir tras logout -> web_proxy install-service --linger"
|
||||||
|
else
|
||||||
|
err "El servicio no arranco. Revisa: web_proxy logs"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_uninstall_service() {
|
||||||
|
systemctl --user disable --now "$SERVICE_NAME" 2>/dev/null
|
||||||
|
rm -f "$SYSTEMD_USER_DIR/$SERVICE_NAME"
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
ok "Servicio $SERVICE_NAME desinstalado."
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_logs() {
|
||||||
|
if service_active || systemctl --user cat "$SERVICE_NAME" &>/dev/null; then
|
||||||
|
journalctl --user -u "$SERVICE_NAME" -n "${1:-40}" --no-pager
|
||||||
|
else
|
||||||
|
local out
|
||||||
|
out="$(conf_get OUT "$DEFAULT_OUT")"
|
||||||
|
[[ -f "$out/mitmdump.log" ]] && tail -n "${1:-40}" "$out/mitmdump.log" \
|
||||||
|
|| info "No hay logs (servicio no instalado, sin log manual)."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
web_proxy — proxy de interceptacion HTTP/HTTPS liviano, siempre activo (mitmproxy)
|
||||||
|
|
||||||
|
USO: web_proxy <comando> [opciones]
|
||||||
|
|
||||||
|
Proxy:
|
||||||
|
start [--port N] [--out DIR] [--rotate-min N] Arranca el proxy (background, manual)
|
||||||
|
stop Para el proxy manual
|
||||||
|
restart [opciones de start] Reinicia el proxy manual
|
||||||
|
status Estado: proxy, servicio, capturas, CA
|
||||||
|
|
||||||
|
Servicio (siempre activo, systemd --user):
|
||||||
|
install-service [--port N] [--out DIR] [--rotate-min N] [--linger]
|
||||||
|
Instala + arranca como servicio
|
||||||
|
stop-service / uninstall-service Para / desinstala el servicio
|
||||||
|
logs [N] Ultimas N lineas de log
|
||||||
|
|
||||||
|
Navegacion:
|
||||||
|
browser [--url URL] [--port N] Lanza Chromium proxeado (perfil aislado)
|
||||||
|
ca Instrucciones para confiar en el CA (HTTPS)
|
||||||
|
|
||||||
|
Consultar capturas:
|
||||||
|
query [FILTRO] [--all|--last] Vuelca flujos (default: ultima captura)
|
||||||
|
Filtros mitmproxy: "~m POST", "~c 500", "~d host"
|
||||||
|
har [SALIDA.har] [--all] Exporta a HAR
|
||||||
|
inspect [ARCHIVO.mitm] Abre UI web (mitmweb) sobre una captura
|
||||||
|
|
||||||
|
Config:
|
||||||
|
WEB_PROXY_HOME directorio de estado (default ~/.web_proxy)
|
||||||
|
Defaults: puerto 8080, capturas ~/captures, rotacion 20 min
|
||||||
|
|
||||||
|
Ejemplo completo:
|
||||||
|
web_proxy install-service --port 8080 --out ~/captures --rotate-min 20
|
||||||
|
web_proxy ca # confiar en el CA para HTTPS
|
||||||
|
web_proxy browser # navega; todo queda capturado
|
||||||
|
web_proxy query "~m POST" # revisa los POST de la ultima ventana
|
||||||
|
web_proxy inspect # UI visual de la ultima captura
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Dispatch ----------------------------------------------------------------
|
||||||
|
|
||||||
|
main() {
|
||||||
|
local cmd="${1:-help}"
|
||||||
|
shift || true
|
||||||
|
case "$cmd" in
|
||||||
|
start) cmd_start "$@" ;;
|
||||||
|
stop) cmd_stop "$@" ;;
|
||||||
|
restart) cmd_restart "$@" ;;
|
||||||
|
status) cmd_status "$@" ;;
|
||||||
|
browser) cmd_browser "$@" ;;
|
||||||
|
query) cmd_query "$@" ;;
|
||||||
|
har) cmd_har "$@" ;;
|
||||||
|
inspect) cmd_inspect "$@" ;;
|
||||||
|
ca) cmd_ca "$@" ;;
|
||||||
|
install-service) cmd_install_service "$@" ;;
|
||||||
|
stop-service) systemctl --user stop "$SERVICE_NAME" && ok "Servicio parado." ;;
|
||||||
|
uninstall-service) cmd_uninstall_service "$@" ;;
|
||||||
|
logs) cmd_logs "$@" ;;
|
||||||
|
help|-h|--help) usage ;;
|
||||||
|
*) err "Comando desconocido: $cmd"; echo; usage; return 2 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user