Documentación Oficial de Ingeniería
MetaProxy v2
Manual Técnico y de Arquitectura
Especificación Integral, Motor de Reescritura, Egress Isolation y Guía de Despliegue
1. Visión General y Objetivos del Proyecto
MetaProxy v2 es una plataforma proxy inversa multitenant de alto rendimiento diseñada por Metabiblioteca para reemplazar la infraestructura existente basada en MuseProxy y BunkerWeb. Su propósito primordial es facultar el acceso remoto y seguro a recursos de investigación científica (Elsevier ScienceDirect, EBSCOhost, SpringerLink, JSTOR, ProQuest, vLex, eLibro) asegurando que el tráfico de cada institución salga a Internet exclusivamente por su dirección IP pública autorizada (Egress Isolation) y que la navegación del usuario permanezca confinada bajo el dominio institucional.
Objetivo Crítico (RNF-001): Ningún paquete transmitido por el usuario de una universidad puede salir a Internet por la dirección IP de otra universidad. El aislamiento físico de sockets y las guardias a nivel de kernel garantizan la seguridad de las suscripciones editoriales.
2. Arquitectura de Alto Nivel y Componentes
El sistema se organiza en tres componentes ejecutables independientes:
- metaproxy-runtime (Data Plane): Motor de proxy inverso en streaming ejecutado como un proceso aislado por tenant bajo systemd (
metaproxy-runtime@<tenant>.service).
- metaproxy-admin (Control Plane): Servidor central con API REST y panel de control web SPA embebido (
//go:embed) en el puerto 9443.
- mpctl (CLI Administrativa): Herramienta de consola para validar perfiles YAML, compilar reglas y calcular hashes criptográficos Argon2id.
INTERNET / USUARIOS REMOTOS
│
▼
┌────────────────────────────────────────────────────────┐
│ Firewall perimetral nftables (tmpproxy-04) │
│ Puertos públicos: 2828 (SSH VPN), 80/443 (HTTP/TLS) │
└────────────────────────────────────────────────────────┘
│
┌────────────────────────┴────────────────────────┐
▼ ▼
┌───────────────────────────────┐ ┌───────────────────────────────┐
│ metaproxy-runtime (t07) │ │ metaproxy-runtime (t08) │
│ Dominio: *.temp-proxy07 │ │ Dominio: *.temp-proxy08 │
│ Bind: eth0 (54.36.18.254) │ │ Bind: eth1 (54.36.18.253) │
│ cgroup: mp-t07.slice │ │ cgroup: mp-t08.slice │
└───────────────────────────────┘ └───────────────────────────────┘
│ │
│ SO_BINDTODEVICE │ SO_BINDTODEVICE
▼ ▼
┌───────────────────────────────┐ ┌───────────────────────────────┐
│ eth0 (54.36.18.254) │ │ eth1 (54.36.18.253 - tabla 1008)
└───────────────────────────────┘ └───────────────────────────────┘
│ │
▼ ▼
[ScienceDirect] [EBSCOhost]
(Reconoce IP .254) (Reconoce IP .253)
3. Aislamiento de Red y Egress Isolation (RNF-001)
El servidor tmpproxy-04 dispone de dos interfaces de red físicas independientes:
| Tenant | Dominio Base | Interfaz Física | IP Egress | Tabla de Ruteo |
| t07 | temp-proxy07.metaproxy.org | eth0 | 54.36.18.254 | main (default) |
| t08 | temp-proxy08.metaproxy.org | eth1 | 54.36.18.253 | 1008 (RoutingPolicyRule) |
Enlace de Socket en Go:
func BindToInterface(fd uintptr, iface string) error {
return unix.SetsockoptString(int(fd), unix.SOL_SOCKET, unix.SO_BINDTODEVICE, iface)
}
Guardia en nftables por cgroupv2:
socket cgroupv2 level 1 "mp-t07.slice" oifname != "lo" ip saddr != 54.36.18.254 counter drop
socket cgroupv2 level 1 "mp-t08.slice" oifname != "lo" ip saddr != 54.36.18.253 counter drop
4. Motor de Reescritura Determinista
El motor de reescritura en Go garantiza que ninguna petición se fugue a la editorial original sin pasar por el proxy:
- Streaming HTML Tokenizer: Basado en
golang.org/x/net/html, procesa etiquetas en flujo continuo sin almacenar páginas completas en RAM.
- CSS & JS Lexical Rewrite: Analiza directivas
url(...) y literales de URLs en scripts.
- Shim JS Inyectado: Intercepta en el cliente las llamadas a
window.fetch, XMLHttpRequest y asignaciones de window.location.
- Aislamiento de Cookies: Cookies de sesión de portal restringidas al apex (
mp_sid) y cookies de host firmadas con HMAC (mp_hs) emitidas mediante tickets de un solo uso.
[ Flujo de Login Institucional y Canje de Ticket (Cookie Vault) ]
+---------------------------------------------------------------------------------------+
| FLUJO DE LOGIN INSTITUCIONAL Y CANJE DE TICKET (COOKIE VAULT) |
| |
| [Navegador] [Apex Portal] [Auth Engine] [Valkey :6379] [Sub] |
| | | | | | |
| 1. |-- GET /login ----->| | | | |
| 2. |<- Formulario HTML -| | | | |
| 3. |-- POST /login ---->| | | | |
| | (user, pass) |-- Verificar ------->| | | |
| 4. | |<- Argon2id OK ------| | | |
| 5. | |-- Crear sess:t07:
(TTL 8h) --------->| | |
| 6. |<- 302 Found (Set-Cookie: mp_sid=; HttpOnly; Secure)-------| | |
| | | | | | |
| 7. |-- GET /go/ebsco -->| | | | |
| | (con mp_sid) |-- Validar sesión ------------------>| | |
| 8. | |-- Crear ticket:t07: (TTL 60s) ------>| | |
| 9. |<- 302 a subdominio: search-ebscohost-com.temp-proxy07...?ticket= | |
| | | | | | |
| 10. |-- GET /login.aspx?ticket= ------------------------------------------>| |
| 11. | | | Consumir y borrar ticket --->| |
| 12. | | |<- Retorna session_id ----------| |
| 13. |<- Set-Cookie: mp_hs=; HttpOnly; Secure; Path=/ --------------| |
| 14. |<- 200 OK HTML Reescrito (Enlaces dentro de *.temp-proxy07.metaproxy.org) -| |
+---------------------------------------------------------------------------------------+
[ Motor de Reescritura Determinista en Streaming ]
+---------------------------------------------------------------------------------------+
| MOTOR DE REESCRITURA DETERMINISTA EN STREAMING Y NAVEGACION PERSISTENTE |
| |
| [Navegador] [Apache 2.4 :443] [Runtime Go :8087] [Upstream Ed] |
| | | | | |
| 1. |-- GET https://www-sciencedirect-com.temp-proxy07.../science/article/123 -->| |
| 2. | (Cookie: mp_hs) |-- ProxyPass http://127.0.0.1:8087 --------> | |
| | | (ProxyPreserveHost On)| | |
| 3. | | |-- Resolver | |
| | | | Host: sciencedirect.com |
| | | | Acción: PROXY | |
| 4. | | |-- GET article --> | |
| | | | (SO_BINDTODEVICE="eth0") |
| | | | (Egress: 54.36.18.254) |
| 5. | | |<- 200 OK (gzip original) ---|
| 6. | | |-- Streaming Tokenizer |
| | | | - Descomprimir gzip |
| | | | - Reescribir href/src |
| | | | - Inyectar Shim JS |
| | | | - Recomprimir gzip |
| 7. | | |-- Logpipe Asíncrono |
| | | | (TipoAccion 2 - Metadatos)|
| 8. | |<- 200 OK Stream --------| | |
| 9. |<- 200 OK (HTML Reescrito servido al usuario) ---| | |
+---------------------------------------------------------------------------------------+
5. API REST del Plano de Control
| Método | Ruta | Descripción |
GET | /healthz | Verificación de salud del servidor (200 OK). |
GET | /api/status | Métricas generales, conteo de tenants y perfiles activos. |
GET | /api/tenants | Listado de instituciones y configuraciones de red. |
GET | /api/profiles | Catálogo de perfiles declarativos cargados. |
POST | /api/profiles/validate | Compilación y ejecución de tests unitarios de perfiles. |
POST | /api/profiles/test-match | Evaluación de enrutamiento y reescritura sobre una URL dada. |
GET | /api/sessions | Inspección de sesiones de usuario activas en Valkey. |
GET | /api/analytics | Estadísticas de uso por TipoAccion (MuseProxy 0..13). |
GET | /docs/* | Manuales HTML, Markdown y descarga directa de archivos Word (.docx). |
6. Infraestructura de Red, DNS y Terminación TLS (Apache + Certbot)
MetaProxy requiere resolver cualquier subdominio codificado (*.temp-proxy07.metaproxy.org y *.temp-proxy08.metaproxy.org) hacia la IP del servidor y terminar TLS con certificados comodín válidos.
[ Infraestructura de Red, DNS Wildcard y Terminación TLS ]
+---------------------------------------------------------------------------------------+
| INFRAESTRUCTURA DE RED, DNS WILDCARD Y TERMINACION TLS (APACHE + CERTBOT) |
| |
| [ Cliente / Navegador ] |
| | |
| | 1. Consulta DNS: *.temp-proxy07.metaproxy.org |
| v |
| +-------------------------------------------------------------------------------+ |
| | GoDaddy DNS Autoritativo (Zona metaproxy.org) | |
| | temp-proxy07.metaproxy.org A 54.36.18.254 | |
| | *.temp-proxy07.metaproxy.org A 54.36.18.254 (Wildcard) | |
| | temp-proxy08.metaproxy.org A 54.36.18.253 | |
| | *.temp-proxy08.metaproxy.org A 54.36.18.253 (Wildcard) | |
| +-------------------------------------------------------------------------------+ |
| | |
| | 2. Petición HTTPS entrante (TCP :443) |
| v |
| +-------------------------------------------------------------------------------+ |
| | Apache 2.4 Reverse Proxy (tmpproxy-04) | |
| | VirtualHost 54.36.18.254:443 (Tenant 07) -> Certificado Let's Encrypt / ECDSA| |
| | VirtualHost 54.36.18.253:443 (Tenant 08) -> Certificado Let's Encrypt / ECDSA| |
| | ProxyPreserveHost On | WebSocket WSS Tunnel | Cabeceras X-Forwarded-* | |
| +-------------------------------------------------------------------------------+ |
| | ^ |
| | ProxyPass http://127.0.0.1:8087 | Recarga en caliente |
| v | (systemctl reload apache2) |
| +------------------------------------+ +--------------------------------------+ |
| | metaproxy-runtime@t07 (:8087) | | Certbot DNS-01 (obtain-wildcard.sh) | |
| | Data Plane Go | | Desafío TXT vía GoDaddy API / Manual | |
| +------------------------------------+ +--------------------------------------+ |
+---------------------------------------------------------------------------------------+
6.1 Configuración de Nombres de Dominio (DNS Wildcard)
| Nombre de Host | Tipo | Destino (IP Pública) | Tenant Asignado | Interfaz |
temp-proxy07.metaproxy.org | A | 54.36.18.254 | t07 (Apex) | eth0 |
*.temp-proxy07.metaproxy.org | A | 54.36.18.254 | t07 (Wildcard) | eth0 |
temp-proxy08.metaproxy.org | A | 54.36.18.253 | t08 (Apex) | eth1 |
*.temp-proxy08.metaproxy.org | A | 54.36.18.253 | t08 (Wildcard) | eth1 |
6.2 Apache VirtualHost con SSL Reverse Proxy
Implementado en deploy/apache/metaproxy-wildcard.conf:
- VirtualHosts exclusivos enlazados por IP pública (
54.36.18.254:443 y 54.36.18.253:443).
- Redirección HTTP 301 global a HTTPS preservando host proxificado y path exacto.
ProxyPreserveHost On para conservar cabecera Host requerida por hostmap.
- Enrutamiento condicional para WebSocket (
Upgrade: websocket → ws://127.0.0.1:8087/).
- Reenvío HTTP inverso a
http://127.0.0.1:8087/ con timeout=120 y retry=0.
6.3 Automatización de Certificados con Certbot (DNS-01 GoDaddy)
Para certificados comodín, MetaProxy utiliza el desafío DNS-01 mediante deploy/certbot/obtain-wildcard.sh:
certbot certonly \
--dns-godaddy \
--dns-godaddy-credentials /etc/letsencrypt/godaddy.ini \
--dns-godaddy-propagation-seconds 60 \
-d "$DOM" -d "*.$DOM" \
--non-interactive --agree-tos -m "soporte@metabiblioteca.com.co" \
--deploy-hook "/bin/systemctl reload metaproxy-runtime@$TENANT.service"
Los certificados emitidos se sincronizan en /var/lib/metaproxy/<tenant>/tls/ con permisos 0640/0600.
6.4 Estrategia de Fallback TLS On-Demand y Autofirmado
Para entornos de staging, pruebas de laboratorio o contingencias en la API de DNS:
- OpenSSL Autofirmado: Si no existe el certificado de Let's Encrypt al arrancar,
setup-apache.sh genera un par TLS ECDSA prime256v1 con SAN comodín para garantizar operatividad inmediata.
- Emisión Nativa Go:
mpctl gen-selfsigned -cert <ruta> -key <ruta> -hosts <hosts> genera certificados X.509 v3 sin dependencias del sistema operativo.
- Hot-Reload sin Downtime: Tanto Apache como
metaproxy-runtime soportan recarga en caliente de certificados sin interrumpir conexiones TCP activas.
7. Comandos de Operación y Administración
# Iniciar servicio de runtime para un tenant
systemctl start metaproxy-runtime@t07.service
# Recargar configuración y certificados en caliente (sin corte)
systemctl reload metaproxy-runtime@t07.service
# Recargar servidor Apache
systemctl reload apache2
# Validar catálogo de perfiles offline con mpctl
./bin/mpctl validate -d profiles ebsco sciencedirect springer jstor proquest
# Iniciar servidor de administración y panel web
./bin/metaproxy-admin -listen 0.0.0.0:9443 -profiles profiles -tenants deploy/tenants