PoC local (On-premises) de OPSWAT MetaDefender integrado con F5 BIG-IP vía protocolo ICAP, con foco en proteger un portal documental / NAS con latencia aceptable para el usuario final.
Ficheros de cliente final (Adobe, Imágenes, Office) que llegan desde el exterior al portal documental y se almacenan en el NAS. El objetivo es sanear (con Deep CDR) y validar el tipo real de fichero antes de persistirlo, manteniendo el flujo transparente al desarrollo y controlando la latencia de subida.
┌─────────────┐ HTTPS ┌─────────────────┐
│ Cliente │ ─────────────► │ Portal Web / │
│ (usuario │ │ App Backend │
│ externo) │ └────────┬────────┘
└─────────────┘ │
│ HTTP/HTTPS upload
▼
┌─────────────────┐
│ F5 │
│ │
│ iRule: │
│ · Steering por │
│ URL/endpoint │
│ · Content-Len │
│ · Tipo fichero │
└────────┬────────┘
│ ICAP REQMOD
│ TCP/1344
▼
┌─────────────────┐
│ MetaDefender │
│ ICAP Server │
│ v5.13.0 │
│ :1344 (ICAP) │
│ :8048 (UI) │
└────────┬────────┘
│ REST API
│ HTTP/8008
▼
┌─────────────────┐
│ MetaDefender │
│ Core │
│ │
│ · Deep CDR │
│ · Multi-AV │
│ · True File │
│ Type check │
└────────┬────────┘
│
┌─────────────┴─────────────┐
▼ ▼
✅ ALLOW ❌ BLOCK
Fichero sanitizado · True type mismatch
(CDR aplicado) · Malware detectado
retorna a F5 · Tipo no permitido
→ NAS / flujo normal → HTTP 403 al cliente
| Componente | Requisito mínimo | Usado en PoC |
|---|---|---|
| CPU | 4 cores | 4 cores (VMware) |
| RAM | 4 GB | 8 GB |
Disco / |
20 GB libres | 50 GB libres |
| SO | RHEL 8/9 x86_64 | RHEL 9.7 (Plow) |
| Virtualización | Bare metal / VMware | VMware STD8/Pro25 |
| Kernel | 4.x+ | 5.14.0-611.42.1.el9_7 |
| systemd | 252+ | 252 |
| Suscripción RHEL | Activa (BaseOS + AppStream) | Simple Content Access |
| Puerto | Protocolo | Uso |
|---|---|---|
| 1344 | TCP | ICAP sin TLS (F5 → ICAP Server) |
| 1345 | TCP | ICAP con TLS |
| 8048 | TCP | Management UI (nginx) |
| 5433 | TCP | PostgreSQL bundleado (solo localhost) |
| Tipo | Extensiones | CDR |
|---|---|---|
| Adobe PDF | ✅ Deep CDR | |
| Imágenes | .jpg .jpeg .png | ✅ CDR básico |
| Office | .doc .docx .xls .xlsx .odt | ✅ Deep CDR |
| Excluido | .pptx .ppt | ❌ Bloqueado en F5 |
| Cualquier otro | — | ❌ Bloqueado en F5 |
El paquete mdicapsrv bundlea internamente todas sus dependencias críticas en /usr/lib/mdicapsrv/:
- Qt 6.9.3 (Core, Network, Sql, Xml, Qml, Concurrent)
- libicudata/libicuuc 73.2
- PostgreSQL propio en
/usr/lib/mdicapsrv/postgres/ - nginx propio
- luajit 2.1
- libcurl, libcrypto, libssl (versiones propias)
Dependencias que SÍ se instalan desde repos del sistema:
sudo dnf install -y openssl-libs libcurl libxcrypt-compat libnsl libpq libev luajit
sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpmcat /etc/redhat-release
hostnamectl
ip addr show | grep "inet " | grep -v 127
nproc && free -h && df -h /
subscription-manager status
sudo dnf repolistsudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
sudo dnf install -y openssl-libs libcurl libxcrypt-compat libnsl libpq libev luajitsudo firewall-cmd --permanent --add-port=1344/tcp
sudo firewall-cmd --permanent --add-port=1345/tcp
sudo firewall-cmd --permanent --add-port=8048/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-portssudo setenforce 0
getenforce # debe mostrar Permissivesudo mkdir -p /etc/mdicapsrv
sudo tee /etc/mdicapsrv/mdicapsrv.ignition > /dev/null << 'EOF'
[dbserver]
type=local
host=localhost
port=5432
user=postgres
password=<CONTRASEÑA_SEGURA_ASCII_SIN_UNICODE>
EOF
sudo chmod 640 /etc/mdicapsrv/mdicapsrv.ignition
sudo chown root:root /etc/mdicapsrv/mdicapsrv.ignition
⚠️ El fichero debe existir ANTES de ejecutar el RPM. Solo caracteres ASCII enuserypassword. Sin Unicode.
wget https://metascanbucket.s3.amazonaws.com/Metadefender/ICAP_Server/5.13.0-1/mdicapsrv-5.13.0-2.x86_64.rpm \
--no-check-certificate \
-O mdicapsrv-5.13.0-2.x86_64.rpm
# Verificar checksum (obtener SHA256 del portal OPSWAT)
echo "<SHA256_DEL_PORTAL> mdicapsrv-5.13.0-2.x86_64.rpm" | sha256sum --checksudo dnf localinstall -y mdicapsrv-5.13.0-2.x86_64.rpm 2>&1 | tee /tmp/mdicapsrv-install.logEl instalador leerá el ignition file y configurará PostgreSQL automáticamente.
sudo systemctl status mdicapsrv mdicapsrv-pg --no-pager
sudo ss -tlnp | grep -E "1344|8048|5433"Acceder a http://<IP>:8048 y completar el wizard de setup inicial.
# Eliminar password del ignition file
sudo sed -i '/^password=/d' /etc/mdicapsrv/mdicapsrv.ignition
# Restaurar SELinux Enforcing
sudo setenforce 1
getenforce| Tab | Parámetro | Valor configurado |
|---|---|---|
| Request Filters | HTTP Body Size | < 10 MB |
| File Type | Engine timeout | 5 min |
| File Type | Engine failure | Block |
| Scan | Allow Scan | ON |
| Scan | Scan Target | MDCore_cdr_profile |
| Scan | Scan Timeout | 30 seg |
| Data Trickling | Enable | OFF (incompatible con CDR) |
| Content Encoded | Scan Content | Scan all content |
| Content Encoded | Base64 failure | Block |
| Content Encoded | Limit File Size | 10 MB |
| Advanced | Override Processing History | ✅ |
| Advanced | Override Client IP by Header | ✅ X-Client-IP / ICAP Header |
| Parámetro | Valor |
|---|---|
| Type | MetaDefender Core |
| Core URL | http://<CORE_IP>:8008/ |
| Workflow | ICAP Deep CDR |
| Local Scan | Deshabilitado |
| Webhook Callbacks | Deshabilitado |
| Preference order | Failover |
Resultado: BLOCKED ✅
HTTP response: 403 Forbidden
X-Infection-Found: Virus/EICAR_Test_File
X-Violations-Found: 1
Motores AV: 15/21 detecciones
Processing Time Core: 36 ms
Resultado: Allowed ✅
X-Response-Info: Allowed
CDR aplicado: documento_sanitized_by_OPSWAT_MetaDefender_<hash>.pdf
Latencia total: 332 ms
| Escenario | Latencia | Umbral PoC | Estado |
|---|---|---|---|
| Fichero infectado (bloqueo) | ~36 ms | 2.000 ms | ✅ |
| PDF limpio + CDR (<1KB) | 332 ms | 2.000 ms | ✅ |
| PDF real estimado (<10MB) | 500–1.200 ms | 2.000 ms | ✅ Proyección |
La iRule de steering selectivo debe implementarse en el F5 con la siguiente lógica:
when HTTP_REQUEST {
# Steering selectivo: solo endpoints de subida de ficheros
if { [HTTP::uri] starts_with "/upload" or
[HTTP::uri] starts_with "/api/documents" } {
# Solo ficheros dentro del límite de tamaño PoC
if { [HTTP::header "Content-Length"] < 10485760 } {
# Activar inspección ICAP REQMOD
ICAP::enable reqmod
# Propagar IP real del cliente al ICAP Server
ICAP::header insert "X-Client-IP" [IP::client_addr]
}
}
}
when ICAP_REQUEST {
# Configurar el servidor ICAP
ICAP::server "icap://<ICAP_IP>:1344/mdicapsrv"
}
⚠️ Adaptar los endpoints/uploady/api/documentsa los paths reales del portal documental.
Síntoma: El instalador pregunta interactivamente por los datos de BD.
Causa: El archivo no existía antes de lanzar el RPM, o el directorio /etc/mdicapsrv/ no existía.
Solución: Crear el directorio y el archivo ANTES del dnf localinstall.
Síntoma: Dependencias no resueltas en rpm -qRp.
Causa: RHEL 9.7 tiene libicu-67. El RPM requiere .so.73.
Solución: No instalar libicu73 externa. OPSWAT bundlea libicudata.so.73 y libicuuc.so.73
en /usr/lib/mdicapsrv/. El dry-run --setopt=tsflags=test pasa correctamente.
Síntoma: http://<IP>:8048/ muestra error 404.
Causa: Es una SPA con hash routing. curl no ejecuta JavaScript.
Solución: Usar http://<IP>:8048 en un navegador real. La SPA carga index.html y el
router maneja /#/.
Síntoma: mdicapsrv en estado failed con SELinux Enforcing.
Solución:
sudo setenforce 0
sudo systemctl restart mdicapsrv
sudo ausearch -m avc -ts today | grep mdicapsrv | audit2allow -M mdicapsrv_poc
sudo semodule -i mdicapsrv_poc.pp
sudo setenforce 1Síntoma: rpm -q postgresql-libs devuelve not installed.
Causa: En RHEL 9 el paquete se llama libpq, no postgresql-libs.
Solución: Instalar libpq — es el equivalente correcto.
Síntoma: Los logs del ICAP muestran la IP del F5, no la del cliente real.
Causa: El campo Name en Advanced → Override Client IP no está configurado,
o la iRule del F5 usa un nombre de header diferente.
Solución: Verificar que la iRule inserta X-Client-IP y que en el workflow
Advanced está marcado Override Client IP by Header con Name = X-Client-IP.
| Log | Ruta | Contenido |
|---|---|---|
| ICAP Server | /var/log/mdicapsrv/mdicapsrv.log |
Requests, respuestas, errores |
| nginx (UI) | /var/log/mdicapsrv/nginx-mdicapsrv.log |
Acceso UI de gestión |
| PostgreSQL | /var/lib/mdicapsrv/pg_data/log/ |
BD interna |
| Upgrade/Install | /var/log/mdicapsrv/mdicap-upgrade-db_*.log |
Migraciones BD |
| Core (escaneos) | http://<CORE_IP>:8008 → Processing History |
Resultados AV + CDR |
Rotación de logs: Preconfigurada por OPSWAT — daily, 30 días, comprimido.
| Criterio | Origen detección | Acción |
|---|---|---|
| Malware detectado por multi-AV | MetaDefender Core | BLOCK → HTTP 403 |
| True File Type mismatch | ICAP Server (FileType engine) | BLOCK → HTTP 403 |
| Fichero fuera de tipo permitido | F5 (primario) | BLOCK en F5 |
| Fichero fuera de tipo permitido | MetaDefender Core (secundario) | BLOCK → HTTP 403 |
| Core inalcanzable (timeout 30s) | ICAP Server | BLOCK (fail-secure) |
| FileType engine failure | ICAP Server | BLOCK (fail-secure) |
| Base64 decoding error | ICAP Server | BLOCK |
| Encoding no soportado | ICAP Server | BLOCK |
| Fichero > 10 MB | F5 / ICAP Server | BLOCK / Skip ICAP |
⚠️ IPs y credenciales sanitizadas. Sustituir por valores reales en cada despliegue.
| Componente | Valor (sanitizado) |
|---|---|
| ICAP Server IP | <ICAP_SERVER_IP> |
| ICAP Server hostname | icap5 |
| MetaDefender Core IP | <CORE_IP> |
| ICAP Server versión | mdicapsrv-5.13.0-2.x86_64 |
| Management UI | http://<ICAP_SERVER_IP>:8048 |
| ICAP endpoint | icap://<ICAP_SERVER_IP>:1344/mdicapsrv |