Despliegue Empresarial de Qwen3-VL-8B: Configuración de Certificados TLS, Redirección Forzada a HTTPS y Activación de Registros de Auditoría

1. Introducción al proyecto

El sistema de chat AI Qwen3-VL-8B es una solución web completa que integra una interfaz frontal, un servidor proxy inverso y un motor de inferencia vLLM. Diseñado con una arquitectura modular, permite despliegue local y acceso remoto, ofreciendo servicios de diálogo con IA seguros y confiables para entornos empresariales.

En un contexto corporativo, solo ejecutar el servicio no basta. La seguridad de datos, el cifrado de comunicaciones y la auditoría de operaciones son requisitos críticos. Este artículo detalla cómo configurar certificados TLS, implementar redirección forzada a HTTPS y habilitar un registro de auditoría completo para el sistema Qwen3-VL-8B.

Componentes principales del sistema:

  • Interfaz frontal: Chat moderno basado en HTML/CSS/JS
  • Servidor proxy: Proxy inverso escrito en Python que maneja archivos estáticos y reenvía API
  • Motor de inferencia vLLM: Backend de alto rendimiento que ejecuta el modelo Qwen3-VL-8B

2. Preparación del entorno y configuración base

2.1 Requisitos del sistema

  • Ubuntu 20.04+ o CentOS 8+
  • Python 3.8+
  • Nginx 1.18+ (para proxy HTTPS)
  • OpenSSL
  • Al menos 8 GB de VRAM
  • Nombre de dominio (para solicitud de certificado)

2.2 Instalación de dependencias

sudo apt update && sudo apt upgrade -y
sudo apt install nginx openssl certbot python3-certbot-nginx -y
python3 --version
pip3 --version
pip3 install requests flask cryptography

2.3 Estructura de directorios recomendada

/opt/qwen-chat/
├── app/               # directorio principal de la aplicación
│   ├── chat.html      # interfaz de chat
│   ├── proxy_server.py
│   └── static/        # recursos estáticos
├── ssl/               # certificados SSL
│   ├── cert.pem
│   ├── privkey.pem
│   └── chain.pem
├── logs/              # registros
│   ├── access.log
│   ├── error.log
│   └── audit.log
└── config/
    ├── nginx.conf
    └── supervisor.conf

3. Configuración de certificados TLS

3.1 Métodos de obtención de certificados

Certificado gratuito Let's Encrypt (para entornos de prueba):

sudo certbot --nginx -d tudominio.com

Certificado comercial (para producción): Adquirido de una CA de confianza.

Certificado autofirmado (para pruebas internas):

openssl req -x509 -newkey rsa:4096 -keyout privkey.pem -out cert.pem \
  -days 365 -nodes -subj "/CN=tudominio.com"

3.2 Instalación y configuración del certificado en Nginx

# /etc/nginx/sites-available/qwen-chat
server {
    listen 80;
    server_name tudominio.com;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    location / {
        return 301 https://$server_name$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name tudominio.com;

    ssl_certificate /etc/letsencrypt/live/tudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/tudominio.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /v1/ {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

3.3 Renovación automática del certificado

sudo crontab -e
# Añadir tarea: renovar cada domingo a las 2:00 AM
0 2 * * 0 /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"

4. Redirección forzada a HTTPS

4.1 Redirección desde Nginx

server {
    listen 80;
    server_name tudominio.com www.tudominio.com;

    location /health {
        proxy_pass http://localhost:8000/health;
        access_log off;
    }

    location / {
        return 301 https://$server_name$request_uri;
    }
}

4.2 Redirección desde la aplicación (segunda capa)

# en proxy_server.py
from flask import request, redirect, Flask

app = Flask(__name__)

@app.before_request
def forzar_https():
    if not request.is_secure and request.host != 'localhost:8000':
        if request.headers.get('X-Forwarded-Proto') != 'https':
            return redirect(request.url.replace('http://', 'https://'), code=301)

4.3 Políticas de seguridad en el frontend

<!-- en chat.html -->
<meta http-equiv="Content-Security-Policy"
      content="upgrade-insecure-requests; default-src 'self' https:">
<meta http-equiv="Strict-Transport-Security"
      content="max-age=31536000; includeSubDomains">

5. Sistema de registro de auditoría

5.1 Requisitos de auditoría

  • Registro de accesos de usuario
  • Registro de llamadas a la API
  • Eventos de seguridad (accesos anómalos, fallos de autenticación)
  • Operaciones del sistema (inicio/parada de servicios, cambios de configuración)

5.2 Implementación del registro de auditoría en el proxy

import logging
import json
from datetime import datetime

logger_auditoria = logging.getLogger('auditoria')
logger_auditoria.setLevel(logging.INFO)
handler_archivo = logging.FileHandler('/opt/qwen-chat/logs/audit.log')
handler_archivo.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
logger_auditoria.addHandler(handler_archivo)

def registrar_evento(tipo_evento, detalles):
    datos = {
        'momento': datetime.utcnow().isoformat(),
        'tipo': tipo_evento,
        'ip_cliente': request.remote_addr,
        'agente': request.headers.get('User-Agent'),
        'detalles': detalles
    }
    logger_auditoria.info(json.dumps(datos))

@app.before_request
def log_solicitud():
    if request.path != '/health':
        registrar_evento('inicio_peticion', {
            'metodo': request.method,
            'ruta': request.path,
            'parametros': dict(request.args)
        })

@app.after_request
def log_respuesta(response):
    if request.path != '/health':
        registrar_evento('fin_peticion', {
            'ruta': request.path,
            'codigo': response.status_code,
            'tamano': response.calculate_content_length()
        })
    return response

5.3 Registro detallado de llamadas a la API

@app.route('/v1/chat/completions', methods=['POST'])
def chat_completar():
    try:
        datos = request.get_json()
        registrar_evento('llamada_api', {
            'endpoint': '/v1/chat/completions',
            'modelo': datos.get('model'),
            'num_mensajes': len(datos.get('messages', [])),
            'max_tokens': datos.get('max_tokens')
        })
        # Reenviar a vLLM
        respuesta = requests.post(
            'http://localhost:3001/v1/chat/completions',
            json=datos,
            timeout=30
        )
        registrar_evento('respuesta_api', {
            'endpoint': '/v1/chat/completions',
            'codigo': respuesta.status_code,
            'tiempo': respuesta.elapsed.total_seconds()
        })
        return respuesta.json(), respuesta.status_code
    except Exception as e:
        registrar_evento('error_api', {
            'endpoint': '/v1/chat/completions',
            'error': str(e)
        })
        return jsonify({'error': str(e)}), 500

5.4 Registro mejorado en Nginx

log_format detallado '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    '$request_time $upstream_response_time '
                    '$ssl_protocol $ssl_cipher '
                    '"$http_x_forwarded_for"';
access_log /var/log/nginx/qwen_access.log detallado;

6. Refuerzo de seguridad y monitorización

6.1 Configuración del firewall

sudo ufw reset
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw default deny incoming
sudo ufw enable

6.2 Script de monitorización

#!/bin/bash
# /opt/qwen-chat/scripts/monitor.sh
verificar_servicio() {
    nombre=$1
    puerto=$2
    if nc -z localhost $puerto; then
        echo "$(date): $nombre en puerto $puerto OK"
    else
        echo "$(date): $nombre en puerto $puerto FALLA"
        # Enviar alerta (email, Slack, etc.)
    fi
}
verificar_servicio "Nginx" 80
verificar_servicio "Nginx HTTPS" 443
verificar_servicio "Proxy" 8000
verificar_servicio "vLLM" 3001
fecha_caducidad=$(openssl x509 -enddate -noout -in /etc/letsencrypt/live/tudominio.com/cert.pem | cut -d= -f2)
echo "Certificado caduca: $fecha_caducidad"

6.3 Rotación de logs

# /etc/logrotate.d/qwen-chat
/opt/qwen-chat/logs/*.log {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    copytruncate
}

7. Despliegue completo

7.1 Script de despliegue automatizado

#!/bin/bash
# deploy_qwen_enterprise.sh
set -e
echo "Iniciando despliegue empresarial de Qwen3-VL-8B..."
mkdir -p /opt/qwen-chat/{app,ssl,logs,config,scripts}
cp chat.html proxy_server.py /opt/qwen-chat/app/
cp start_all.sh /opt/qwen-chat/
apt update && apt install -y nginx openssl certbot python3-certbot-nginx
certbot certonly --standalone -d tudominio.com --non-interactive --agree-tos
cp nginx.conf /etc/nginx/sites-available/qwen-chat
ln -sf /etc/nginx/sites-available/qwen-chat /etc/nginx/sites-enabled/
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
systemctl restart nginx
cd /opt/qwen-chat && ./start_all.sh
cp monitor.sh /opt/qwen-chat/scripts/
cp logrotate-config /etc/logrotate.d/qwen-chat
(crontab -l 2>/dev/null; echo "0 2 * * 0 /usr/bin/certbot renew --quiet --post-hook 'systemctl reload nginx'") | crontab -
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/qwen-chat/scripts/monitor.sh >> /opt/qwen-chat/logs/monitor.log") | crontab -
echo "Despliegue completado. Acceder a: https://tudominio.com"

7.2 Verificación del despliegue

netstat -tlnp | grep -E '(80|443|8000|3001)'
curl -I https://tudominio.com
curl -I https://tudominio.com/chat.html
curl -X POST https://tudominio.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"Qwen3-VL-8B","messages":[{"role":"user","content":"Hola"}]}'
openssl s_client -connect tudominio.com:443 -servername tudominio.com < /dev/null 2>/dev/null | openssl x509 -noout -dates
tail -f /opt/qwen-chat/logs/audit.log
tail -f /var/log/nginx/access.log

7.3 Solución de problemas comunes

  • Fallo en la obtención del certificado: verificar resolución DNS, puerto 80 libre, revisar /var/log/letsencrypt/
  • HTTPS no funciona: ejecuatr nginx -t, verificar rutas de certificados, revisar /var/log/nginx/error.log
  • Los logs de auditoría no se generan: comproabr permisos del directorio logs, permisos de escritura de Python, revisar salida de la aplicación

Etiquetas: Qwen3-VL-8B TLS HTTPS Nginx auditoría

Publicado el 7-1 22:32